/* Copyright (c) 2014-2015, ArrayFire Copyright (c) 2015 Gábor Mező aka unbornchikken (gabor.mezo@outlook.com) All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the ArrayFire nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "ext.h" #include "arraywrapper.h" #include "helpers.h" #include "guard.h" #include "worker.h" #include "errors.h" #include "symbols.h" using namespace v8; using namespace std; using namespace node; Persistent ArrayWrapper::constructor; int GetMemSize(const af::array* array) { // Make GC aware of device memory. // Event it's VRAM this should keep the usage on low (few hundred megabytes), // so we won't triggrer out of memory errors. // TODO: If ArrayFire's CUDA error handling gets fixed, // TODO: then we should only report memory usage for CPU based devices. return static_cast(sizeof(af::array)) + array->elements() + 200; } ArrayWrapper::ArrayWrapper(af::array* array) : _array(array) { assert(array); NanAdjustExternalMemory(GetMemSize(array)); } ArrayWrapper::~ArrayWrapper() { NanAdjustExternalMemory(-GetMemSize(_array)); delete _array; } void ArrayWrapper::Init(v8::Local exports) { auto tmpl = NanNew(New); tmpl->SetClassName(NanNew("AFArray")); int noOfMethods = 21; tmpl->InstanceTemplate()->SetInternalFieldCount(noOfMethods); NanSetPrototypeTemplate(tmpl, NanNew("elements"), NanNew(Elements), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("host"), NanNew(Host), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("copyToHost"), NanNew(Host), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("scalar"), NanNew(Scalar), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("value"), NanNew(Scalar), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("write"), NanNew(Write), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("type"), NanNew(Type), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("dims"), NanNew(Dims), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("numdims"), NanNew(NumDims), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("numDims"), NanNew(NumDims), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("bytes"), NanNew(Bytes), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("copy"), NanNew(Copy), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("isempty"), NanNew(IsEmpty), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("isEmpty"), NanNew(IsEmpty), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("isscalar"), NanNew(IsScalar), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("isScalar"), NanNew(IsScalar), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("isvector"), NanNew(IsVector), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("isVector"), NanNew(IsVector), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("isrow"), NanNew(IsRow), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("isRow"), NanNew(IsRow), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("iscolumn"), NanNew(IsColumn), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("isColumn"), NanNew(IsColumn), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("iscomplex"), NanNew(IsComplex), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("isComplex"), NanNew(IsComplex), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("isreal"), NanNew(IsReal), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("isReal"), NanNew(IsReal), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("isdouble"), NanNew(IsDouble), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("isDouble"), NanNew(IsDouble), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("issingle"), NanNew(IsSingle), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("isSingle"), NanNew(IsSingle), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("isrealfloating"), NanNew(IsRealFloating), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("isRealFloating"), NanNew(IsRealFloating), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("isfloating"), NanNew(IsFloating), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("isFloating"), NanNew(IsFloating), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("isinteger"), NanNew(IsInteger), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("isInteger"), NanNew(IsInteger), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("isbool"), NanNew(IsBool), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("isBool"), NanNew(IsBool), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("eval"), NanNew(Eval), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("at"), NanNew(At), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("row"), NanNew(Row), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("col"), NanNew(Col), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("slice"), NanNew(Slice), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("rows"), NanNew(Rows), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("cols"), NanNew(Cols), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("slices"), NanNew(Slices), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("as"), NanNew(As), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("assign"), NanNew(Assign), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("set"), NanNew(Assign), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("add"), NanNew(Add), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("addAssign"), NanNew(AddAssign), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("sub"), NanNew(Sub), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("subAssign"), NanNew(SubAssign), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("mul"), NanNew(Mul), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("mulAssign"), NanNew(MulAssign), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("div"), NanNew(Div), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("divAssign"), NanNew(DivAssign), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("bitshiftl"), NanNew(BitShiftL), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("bitShiftL"), NanNew(BitShiftL), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("bitshiftr"), NanNew(BitShiftR), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("bitShiftR"), NanNew(BitShiftR), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("lt"), NanNew(Lt), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("gt"), NanNew(Gt), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("le"), NanNew(Le), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("ge"), NanNew(Ge), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("eq"), NanNew(Eq), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("neq"), NanNew(Neq), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("and"), NanNew(And), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("or"), NanNew(Or), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("bitAnd"), NanNew(BitAnd), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("bitOr"), NanNew(BitOr), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("bitXor"), NanNew(BitXor), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("rhsAdd"), NanNew(RhsAdd), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("rhsSub"), NanNew(RhsSub), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("rhsMul"), NanNew(RhsMul), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("rhsDiv"), NanNew(RhsDiv), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("rhsBitshiftl"), NanNew(RhsBitShiftL), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("rhsBitShiftL"), NanNew(RhsBitShiftL), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("rhsBitshiftr"), NanNew(RhsBitShiftR), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("rhsBitShiftR"), NanNew(RhsBitShiftR), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("rhsLt"), NanNew(RhsLt), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("rhsGt"), NanNew(RhsGt), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("rhsLe"), NanNew(RhsLe), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("rhsGe"), NanNew(RhsGe), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("rhsEq"), NanNew(RhsEq), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("rhsNeq"), NanNew(RhsNeq), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("rhsAnd"), NanNew(RhsAnd), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("rhsOr"), NanNew(RhsOr), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("rhsBitAnd"), NanNew(RhsBitAnd), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("rhsBitOr"), NanNew(RhsBitOr), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("rhsBitXor"), NanNew(RhsBitXor), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("neg"), NanNew(Neg), v8::None); NanSetPrototypeTemplate(tmpl, NanNew("not"), NanNew(Not), v8::None); auto f = tmpl->GetFunction(); f->Set(NanNew("create"), NanNew(Create)->GetFunction()); NanAssignPersistent(constructor, f); exports->Set(NanNew("AFArray"), f); } v8::Local ArrayWrapper::New(const af::array& array) { return New(new af::array(array)); } v8::Local ArrayWrapper::New(af::array* array) { assert(array); Local args[] = { WrapPointer(array) }; auto c = NanNew(constructor); auto inst = c->NewInstance(1, args); assert(ObjectWrap::Unwrap(inst)->_array == array); return inst; } void ArrayWrapper::NewAsync(const v8::FunctionCallbackInfo& args, const std::function& arrayFactory) { if (args.Length() >= 1 && args[args.Length() - 1]->IsFunction()) { auto callback = new NanCallback(args[args.Length() - 1].As()); auto worker = new Worker(callback, arrayFactory, [](Worker* w, af::array* a){ return ArrayWrapper::New(a); }); NanAsyncQueueWorker(worker); NanReturnUndefined(); } else { NAN_THROW("Last argument have to be a callback!"); } } af::array* ArrayWrapper::GetArray(v8::Local value) { auto array = TryGetArray(value); if (array) return array; ARRAYFIRE_THROW("Argument is not an AFArray instance."); } af::array* ArrayWrapper::TryGetArray(v8::Local value) { try { if (value->IsObject()) { auto obj = value.As(); if (obj->GetConstructorName()->Equals(NanNew(Symbols::AFArrayClass))) { auto wrapper = ObjectWrap::Unwrap(value.As()); return wrapper->_array; } } } catch (...) { } return nullptr; } af::array* ArrayWrapper::GetArray(v8::Local value) { auto array = TryGetArray(value); if (array) return array; ARRAYFIRE_THROW("Argument is not an AFArray instance."); } af::array* ArrayWrapper::TryGetArray(v8::Local value) { try { auto wrapper = ObjectWrap::Unwrap(value.As()); if (wrapper) return wrapper->_array; } catch (...) { } return nullptr; } af::array* ArrayWrapper::GetArrayAt(const v8::FunctionCallbackInfo& args, int index) { auto array = TryGetArrayAt(args, index); if (array) return array; stringstream ss; ss << "Argument at position " << to_string(index) << ". is not an AFArray instance."; ARRAYFIRE_THROW(ss.str().c_str()); } af::array* ArrayWrapper::TryGetArrayAt(const v8::FunctionCallbackInfo& args, int index) { if (index < args.Length()) { return GetArray(args[index]); } return nullptr; } void ArrayWrapper::New(const v8::FunctionCallbackInfo& args) { NanScope(); try { ArrayWrapper* instance = nullptr; try { if (args.Length() == 0) { Guard(); instance = new ArrayWrapper(new af::array()); } else if (args.Length() == 1) { if (Buffer::HasInstance(args[0])) { instance = new ArrayWrapper(reinterpret_cast(Buffer::Data(args[0]))); } } else { Guard(); auto dimAndType = ParseDimAndTypeArgs(args); instance = new ArrayWrapper(new af::array(dimAndType.first, dimAndType.second)); } } catch (...) { delete instance; throw; } if (!instance) { return NAN_THROW("Invalid arguments."); } instance->Wrap(args.Holder()); NanReturnValue(args.Holder()); } ARRAYFIRE_CATCH } template af::array* ArrayWrapper::CreateArray(void* ptr, af_source src, const af::dim4& dim4) { Guard(); return new af::array(dim4, (T*)ptr, src); } NAN_METHOD(ArrayWrapper::Create) { NanScope(); try { int buffIdx = -1; function factory; for (int i = 0; i < args.Length(); i++) { if (Buffer::HasInstance(args[i])) { buffIdx = i; break; } } if (buffIdx == -1) { return NAN_THROW("Buffer argument expected."); } else if (buffIdx + 1 < args.Length()) { // Copy / wrap ptr // args: dim0..dimn, dtype, ptr[, source] af_source src = afHost; if (buffIdx + 1 < args.Length() && args[buffIdx + 1]->IsNumber()) { src = (af_source)(args[buffIdx + 2]->Int32Value()); } auto buffObj = args[buffIdx]->ToObject(); char* ptr = Buffer::Data(buffObj); auto dimAndType = ParseDimAndTypeArgs(args, buffIdx); switch (dimAndType.second) { case f32: factory = [=]() { return CreateArray(ptr, src, dimAndType.first); }; break; case f64: factory = [=]() { return CreateArray(ptr, src, dimAndType.first); }; break; case s32: factory = [=]() { return CreateArray(ptr, src, dimAndType.first); }; break; case u32: factory = [=]() { return CreateArray(ptr, src, dimAndType.first); }; break; case u8: factory = [=]() { return CreateArray(ptr, src, dimAndType.first ); }; break; case c32: factory = [=]() { return CreateArray(ptr, src, dimAndType.first); }; break; case c64: factory = [=]() { return CreateArray(ptr, src, dimAndType.first); }; break; case b8: factory = [=]() { return CreateArray(ptr, src, dimAndType.first); }; break; case s64: factory = [=]() { return CreateArray(ptr, src, dimAndType.first); }; break; case u64: factory = [=]() { return CreateArray(ptr, src, dimAndType.first); }; break; default: assert(false); } } if (!factory) { return NAN_THROW_INVALID_ARGS(); } auto conv = [](Worker* w, af::array* a) { return New(a); }; auto worker = new Worker(GetCallback(args), move(factory), move(conv)); worker->SaveToPersistent("data", args[buffIdx]->ToObject()); NanAsyncQueueWorker(worker); NanReturnUndefined(); } ARRAYFIRE_CATCH } NAN_METHOD(ArrayWrapper::Elements) { NanScope(); try { Guard(); NanReturnValue(NanNew(GetArray(args.This())->elements())); } ARRAYFIRE_CATCH } NAN_METHOD(ArrayWrapper::Host) { NanScope(); try { ARGS_LEN(1) char* buffData; auto pArray = GetArray(args.This()); if (Buffer::HasInstance(args[0])) { buffData = Buffer::Data(args[0]); if (Buffer::Length(args[0]) < pArray->bytes()) { return NAN_THROW("Buffer is too small to hold values."); } af::array array(*pArray); auto exec = [=]() { Guard(); array.host(buffData); }; auto worker = new Worker(GetCallback(args), move(exec)); worker->SaveToPersistent("data", args[0]->ToObject()); NanAsyncQueueWorker(worker); NanReturnUndefined(); } else { size_t size = pArray->elements() * GetDTypeInfo(pArray->type()).second; buffData = new char[size]; try { af::array array(*pArray); auto exec = [=]() { Guard(); array.host(buffData); return buffData; }; auto conv = [=](Worker* w, char* data) { return NanNewBufferHandle(data, size, [](char* data, void* hint) { delete[] data; }, nullptr); }; auto worker = new Worker(GetCallback(args), move(exec), move(conv)); NanAsyncQueueWorker(worker); NanReturnUndefined(); } catch (...) { delete[] buffData; throw; } } } ARRAYFIRE_CATCH } NAN_METHOD(ArrayWrapper::Scalar) { NanScope(); try { ARGS_LEN(1) auto pArray = GetArray(args.This()); af::array array(*pArray); switch (array.type()) { case f32: { auto exec = [=]() { Guard(); return array.scalar(); }; auto worker = new Worker(GetCallback(args), move(exec)); NanAsyncQueueWorker(worker); } break; case f64: { auto exec = [=]() { Guard(); return array.scalar(); }; auto worker = new Worker(GetCallback(args), move(exec)); NanAsyncQueueWorker(worker); } break; case s32: { auto exec = [=]() { Guard(); return array.scalar(); }; auto worker = new Worker(GetCallback(args), move(exec)); NanAsyncQueueWorker(worker); } break; case u32: { auto exec = [=]() { Guard(); return array.scalar(); }; auto worker = new Worker(GetCallback(args), move(exec)); NanAsyncQueueWorker(worker); } break; case u8: { auto exec = [=]() { Guard(); return array.scalar(); }; auto worker = new Worker(GetCallback(args), move(exec)); NanAsyncQueueWorker(worker); } break; case b8: { auto exec = [=]() { Guard(); return array.scalar(); }; auto worker = new Worker(GetCallback(args), move(exec)); NanAsyncQueueWorker(worker); } break; case c32: { auto exec = [=]() { Guard(); return array.scalar(); }; auto conv = [=](Worker* w, af::cfloat data) { return ToV8Complex(data); }; auto worker = new Worker(GetCallback(args), move(exec), move(conv)); NanAsyncQueueWorker(worker); } break; case c64: { auto exec = [=]() { Guard(); return array.scalar(); }; auto conv = [=](Worker* w, af::cdouble data) { return ToV8Complex(data); }; auto worker = new Worker(GetCallback(args), move(exec), move(conv)); NanAsyncQueueWorker(worker); } break; case s64: { auto exec = [=]() { Guard(); return array.scalar(); }; auto conv = [=](Worker* w, long long data) { return NanNew(to_string(data).c_str()); }; auto worker = new Worker(GetCallback(args), move(exec), move(conv)); NanAsyncQueueWorker(worker); } break; case u64: { auto exec = [=]() { Guard(); return array.scalar(); }; auto conv = [=](Worker* w, unsigned long long data) { return NanNew(to_string(data).c_str()); }; auto worker = new Worker(GetCallback(args), move(exec), move(conv)); NanAsyncQueueWorker(worker); } break; default: assert(false); } NanReturnUndefined(); } ARRAYFIRE_CATCH } NAN_METHOD(ArrayWrapper::Write) { NanScope(); try { ARGS_LEN(3) char* buffData; auto pArray = GetArray(args.This()); if (Buffer::HasInstance(args[0])) { buffData = Buffer::Data(args[0]); } else { return NAN_THROW("First argument is no a Buffer."); } unsigned bytes = args[1]->Uint32Value(); af_source src = afHost; if (args.Length() > 3) { src = (af_source)(args[2]->Int32Value()); } af::array array(*pArray); auto exec = [=]() { Guard(); af_write_array(array.get(), buffData, bytes, src); }; auto worker = new Worker(GetCallback(args), move(exec)); worker->SaveToPersistent("data", args[0]->ToObject()); NanAsyncQueueWorker(worker); NanReturnUndefined(); } ARRAYFIRE_CATCH } NAN_METHOD(ArrayWrapper::Type) { NanScope(); try { NanReturnValue(GetArray(args.This())->type()); } ARRAYFIRE_CATCH } NAN_METHOD(ArrayWrapper::Dims) { NanScope(); try { auto pArray = GetArray(args.This()); if (!args.Length()) { auto dims = pArray->dims(); auto jsDims = NanNew(); jsDims->Set(NanNew(Symbols::Elements), NanNew(dims.elements())); jsDims->Set(NanNew(Symbols::Ndims), NanNew(dims.ndims())); jsDims->Set(NanNew(Symbols::NDims), NanNew(dims.ndims())); auto pDims = NanNew(4); pDims->Set(0, NanNew(dims[0])); pDims->Set(1, NanNew(dims[1])); pDims->Set(2, NanNew(dims[2])); pDims->Set(3, NanNew(dims[3])); jsDims->Set(NanNew(Symbols::Values), pDims); NanReturnValue(jsDims); } else { NanReturnValue(NanNew(pArray->dims(args[0]->Uint32Value()))); } } ARRAYFIRE_CATCH } NAN_METHOD(ArrayWrapper::NumDims) { NanScope(); try { NanReturnValue(NanNew(GetArray(args.This())->numdims())); } ARRAYFIRE_CATCH } NAN_METHOD(ArrayWrapper::Bytes) { NanScope(); try { NanReturnValue(NanNew((unsigned)GetArray(args.This())->bytes())); } ARRAYFIRE_CATCH } NAN_METHOD(ArrayWrapper::Copy) { NanScope(); try { Guard(); NanReturnValue(New(GetArray(args.This())->copy())); } ARRAYFIRE_CATCH } NAN_METHOD(ArrayWrapper::IsEmpty) { NanScope(); try { NanReturnValue(NanNew(GetArray(args.This())->isempty())); } ARRAYFIRE_CATCH } NAN_METHOD(ArrayWrapper::IsScalar) { NanScope(); try { NanReturnValue(NanNew(GetArray(args.This())->isscalar())); } ARRAYFIRE_CATCH } NAN_METHOD(ArrayWrapper::IsVector) { NanScope(); try { NanReturnValue(NanNew(GetArray(args.This())->isvector())); } ARRAYFIRE_CATCH } NAN_METHOD(ArrayWrapper::IsRow) { NanScope(); try { NanReturnValue(NanNew(GetArray(args.This())->isrow())); } ARRAYFIRE_CATCH } NAN_METHOD(ArrayWrapper::IsColumn) { NanScope(); try { NanReturnValue(NanNew(GetArray(args.This())->iscolumn())); } ARRAYFIRE_CATCH } NAN_METHOD(ArrayWrapper::IsComplex) { NanScope(); try { NanReturnValue(NanNew(GetArray(args.This())->iscomplex())); } ARRAYFIRE_CATCH } NAN_METHOD(ArrayWrapper::IsReal) { NanScope(); try { NanReturnValue(NanNew(GetArray(args.This())->isreal())); } ARRAYFIRE_CATCH } NAN_METHOD(ArrayWrapper::IsDouble) { NanScope(); try { NanReturnValue(NanNew(GetArray(args.This())->isdouble())); } ARRAYFIRE_CATCH } NAN_METHOD(ArrayWrapper::IsSingle) { NanScope(); try { NanReturnValue(NanNew(GetArray(args.This())->issingle())); } ARRAYFIRE_CATCH } NAN_METHOD(ArrayWrapper::IsRealFloating) { NanScope(); try { NanReturnValue(NanNew(GetArray(args.This())->isrealfloating())); } ARRAYFIRE_CATCH } NAN_METHOD(ArrayWrapper::IsFloating) { NanScope(); try { NanReturnValue(NanNew(GetArray(args.This())->isfloating())); } ARRAYFIRE_CATCH } NAN_METHOD(ArrayWrapper::IsInteger) { NanScope(); try { NanReturnValue(NanNew(GetArray(args.This())->isinteger())); } ARRAYFIRE_CATCH } NAN_METHOD(ArrayWrapper::IsBool) { NanScope(); try { NanReturnValue(NanNew(GetArray(args.This())->type() == b8)); } ARRAYFIRE_CATCH } NAN_METHOD(ArrayWrapper::Eval) { NanScope(); try { Guard(); GetArray(args.This())->eval(); NanReturnUndefined(); } ARRAYFIRE_CATCH } NAN_METHOD(ArrayWrapper::At) { // Aka "indexing" NanScope(); try { ARGS_LEN(1) Guard(); if (args.Length() == 1) { auto ri = ToRegionIndex(args[0]); switch (get<0>(ri)) { case Region::Row: NanReturnValue(New(GetArray(args.This())->row(get<1>(ri)))); break; case Region::Rows: NanReturnValue(New(GetArray(args.This())->rows(get<1>(ri), get<2>(ri)))); break; case Region::Col: NanReturnValue(New(GetArray(args.This())->col(get<1>(ri)))); break; case Region::Cols: NanReturnValue(New(GetArray(args.This())->cols(get<1>(ri), get<2>(ri)))); break; case Region::Slice: NanReturnValue(New(GetArray(args.This())->slice(get<1>(ri)))); break; case Region::Slices: NanReturnValue(New(GetArray(args.This())->slices(get<1>(ri), get<2>(ri)))); break; default: NanReturnValue(New(GetArray(args.This())->operator()(ToIndex(args[0])))); break; } } else if (args.Length() == 2) { NanReturnValue(New(GetArray(args.This())->operator()(ToIndex(args[0]), ToIndex(args[1])))); } else if (args.Length() == 3) { NanReturnValue(New(GetArray(args.This())->operator()(ToIndex(args[0]), ToIndex(args[1]), ToIndex(args[2])))); } else { NanReturnValue(New(GetArray(args.This())->operator()(ToIndex(args[0]), ToIndex(args[1]), ToIndex(args[2]), ToIndex(args[3])))); } } ARRAYFIRE_CATCH } #define AFARRAY_IMPL_IDX1(F, f)\ NAN_METHOD(ArrayWrapper::F)\ {\ NanScope();\ try\ {\ ARGS_LEN(1)\ Guard();\ auto pArray = GetArray(args.This());\ NanReturnValue(New(pArray->f(args[0]->Int32Value())));\ }\ ARRAYFIRE_CATCH\ } AFARRAY_IMPL_IDX1(Row, row) AFARRAY_IMPL_IDX1(Col, col) AFARRAY_IMPL_IDX1(Slice, slice) #undef AFARRAY_IMPL_IDX1 #define AFARRAY_IMPL_IDX2(F, f)\ NAN_METHOD(ArrayWrapper::F)\ {\ NanScope();\ try\ {\ ARGS_LEN(2);\ Guard();\ auto pArray = GetArray(args.This());\ NanReturnValue(New(pArray->f(args[0]->Int32Value(), args[1]->Int32Value())));\ }\ ARRAYFIRE_CATCH\ } AFARRAY_IMPL_IDX2(Rows, rows) AFARRAY_IMPL_IDX2(Cols, cols) AFARRAY_IMPL_IDX2(Slices, slices) #undef AFARRAY_IMPL_IDX2 NAN_METHOD(ArrayWrapper::As) { NanScope(); try { ARGS_LEN(1); af::dtype type = GetDTypeInfo(args[0]->Uint32Value()).first; Guard(); NanReturnValue(New(GetArray(args.This())->as(type))); } ARRAYFIRE_CATCH } #define AFARRAY_IMPL_ASSIGN(F, Op)\ NAN_METHOD(ArrayWrapper::F)\ {\ NanScope();\ \ try\ {\ auto pArray = GetArray(args.This());\ auto& array = *pArray;\ bool isDouble = NeedsDouble(array);\ ARGS_LEN(1)\ if (args.Length() == 1)\ {\ auto value = args[0];\ auto pOtherArray = TryGetArray(value);\ Guard();\ if (pOtherArray)\ {\ auto& otherArray = *pOtherArray;\ array Op otherArray;\ }\ else if (value->IsNumber())\ {\ double v = value->NumberValue();\ if (floor(v) == v)\ {\ array Op value->Int32Value();\ }\ else if (isDouble)\ {\ array Op v;\ }\ else\ {\ array Op (float)v;\ }\ }\ else if (value->IsObject())\ {\ if (isDouble)\ {\ auto v = ToDComplex(value);\ array Op v;\ }\ else\ {\ auto v = ToFComplex(value);\ array Op v;\ }\ }\ else if (value->IsString())\ {\ String::Utf8Value str(value);\ long long v = strtoll(*str, nullptr, 10);\ array Op v;\ }\ else\ {\ return NAN_THROW_INVALID_ARGS();\ }\ }\ else if (args.Length() == 2)\ {\ auto regIdx = ToRegionIndex(args[0]);\ auto reg = get<0>(regIdx);\ auto value = args[1];\ auto pOtherArray = TryGetArray(value);\ switch(reg)\ {\ case Region::None:\ {\ auto idx0 = ToIndex(args[0]);\ Guard();\ if (pOtherArray)\ {\ auto& otherArray = *pOtherArray;\ array(idx0) Op otherArray;\ }\ else if (value->IsNumber())\ {\ double v = value->NumberValue();\ if (floor(v) == v)\ {\ array(idx0) Op value->Int32Value();\ }\ else if (isDouble)\ {\ array(idx0) Op v;\ }\ else\ {\ array(idx0) Op (float)v;\ }\ }\ else if (value->IsObject())\ {\ if (isDouble)\ {\ auto v = ToDComplex(value);\ array(idx0) Op v;\ }\ else\ {\ auto v = ToFComplex(value);\ array(idx0) Op v;\ }\ }\ else if (value->IsString())\ {\ String::Utf8Value str(value);\ long long v = strtoll(*str, nullptr, 10);\ array(idx0) Op v;\ }\ else\ {\ return NAN_THROW_INVALID_ARGS();\ }\ }\ break;\ case Region::Row:\ {\ Guard();\ if (pOtherArray)\ {\ auto& otherArray = *pOtherArray;\ array.row(get<1>(regIdx)) Op otherArray;\ }\ else if (value->IsNumber())\ {\ double v = value->NumberValue();\ if (floor(v) == v)\ {\ array.row(get<1>(regIdx)) Op value->Int32Value();\ }\ else if (isDouble)\ {\ array.row(get<1>(regIdx)) Op v;\ }\ else\ {\ array.row(get<1>(regIdx)) Op (float)v;\ }\ }\ else if (value->IsObject())\ {\ if (isDouble)\ {\ auto v = ToDComplex(value);\ array.row(get<1>(regIdx)) Op v;\ }\ else\ {\ auto v = ToFComplex(value);\ array.row(get<1>(regIdx)) Op v;\ }\ }\ else if (value->IsString())\ {\ String::Utf8Value str(value);\ long long v = strtoll(*str, nullptr, 10);\ array.row(get<1>(regIdx)) Op v;\ }\ else\ {\ return NAN_THROW_INVALID_ARGS();\ }\ }\ break;\ case Region::Rows:\ {\ Guard();\ if (pOtherArray)\ {\ auto& otherArray = *pOtherArray;\ array.rows(get<1>(regIdx), get<2>(regIdx)) Op otherArray;\ }\ else if (value->IsNumber())\ {\ double v = value->NumberValue();\ if (floor(v) == v)\ {\ array.rows(get<1>(regIdx), get<2>(regIdx)) Op value->Int32Value();\ }\ else if (isDouble)\ {\ array.rows(get<1>(regIdx), get<2>(regIdx)) Op v;\ }\ else\ {\ array.rows(get<1>(regIdx), get<2>(regIdx)) Op (float)v;\ }\ }\ else if (value->IsObject())\ {\ if (isDouble)\ {\ auto v = ToDComplex(value);\ array.rows(get<1>(regIdx), get<2>(regIdx)) Op v;\ }\ else\ {\ auto v = ToFComplex(value);\ array.rows(get<1>(regIdx), get<2>(regIdx)) Op v;\ }\ }\ else if (value->IsString())\ {\ String::Utf8Value str(value);\ long long v = strtoll(*str, nullptr, 10);\ array.rows(get<1>(regIdx), get<2>(regIdx)) Op v;\ }\ else\ {\ return NAN_THROW_INVALID_ARGS();\ }\ }\ break;\ case Region::Col:\ {\ Guard();\ if (pOtherArray)\ {\ auto& otherArray = *pOtherArray;\ array.col(get<1>(regIdx)) Op otherArray;\ }\ else if (value->IsNumber())\ {\ double v = value->NumberValue();\ if (floor(v) == v)\ {\ array.col(get<1>(regIdx)) Op value->Int32Value();\ }\ else if (isDouble)\ {\ array.col(get<1>(regIdx)) Op v;\ }\ else\ {\ array.col(get<1>(regIdx)) Op (float)v;\ }\ }\ else if (value->IsObject())\ {\ if (isDouble)\ {\ auto v = ToDComplex(value);\ array.col(get<1>(regIdx)) Op v;\ }\ else\ {\ auto v = ToFComplex(value);\ array.col(get<1>(regIdx)) Op v;\ }\ }\ else if (value->IsString())\ {\ String::Utf8Value str(value);\ long long v = strtoll(*str, nullptr, 10);\ array.col(get<1>(regIdx)) Op v;\ }\ else\ {\ return NAN_THROW_INVALID_ARGS();\ }\ }\ break;\ case Region::Cols:\ {\ Guard();\ if (pOtherArray)\ {\ auto& otherArray = *pOtherArray;\ array.cols(get<1>(regIdx), get<2>(regIdx)) Op otherArray;\ }\ else if (value->IsNumber())\ {\ double v = value->NumberValue();\ if (floor(v) == v)\ {\ array.cols(get<1>(regIdx), get<2>(regIdx)) Op value->Int32Value();\ }\ else if (isDouble)\ {\ array.cols(get<1>(regIdx), get<2>(regIdx)) Op v;\ }\ else\ {\ array.cols(get<1>(regIdx), get<2>(regIdx)) Op (float)v;\ }\ }\ else if (value->IsObject())\ {\ if (isDouble)\ {\ auto v = ToDComplex(value);\ array.cols(get<1>(regIdx), get<2>(regIdx)) Op v;\ }\ else\ {\ auto v = ToFComplex(value);\ array.cols(get<1>(regIdx), get<2>(regIdx)) Op v;\ }\ }\ else if (value->IsString())\ {\ String::Utf8Value str(value);\ long long v = strtoll(*str, nullptr, 10);\ array.cols(get<1>(regIdx), get<2>(regIdx)) Op v;\ }\ else\ {\ return NAN_THROW_INVALID_ARGS();\ }\ }\ break;\ case Region::Slice:\ {\ Guard();\ if (pOtherArray)\ {\ auto& otherArray = *pOtherArray;\ array.slice(get<1>(regIdx)) Op otherArray;\ }\ else if (value->IsNumber())\ {\ double v = value->NumberValue();\ if (floor(v) == v)\ {\ array.slice(get<1>(regIdx)) Op value->Int32Value();\ }\ else if (isDouble)\ {\ array.slice(get<1>(regIdx)) Op v;\ }\ else\ {\ array.slice(get<1>(regIdx)) Op (float)v;\ }\ }\ else if (value->IsObject())\ {\ if (isDouble)\ {\ auto v = ToDComplex(value);\ array.slice(get<1>(regIdx)) Op v;\ }\ else\ {\ auto v = ToFComplex(value);\ array.slice(get<1>(regIdx)) Op v;\ }\ }\ else if (value->IsString())\ {\ String::Utf8Value str(value);\ long long v = strtoll(*str, nullptr, 10);\ array.slice(get<1>(regIdx)) Op v;\ }\ else\ {\ return NAN_THROW_INVALID_ARGS();\ }\ }\ break;\ case Region::Slices:\ {\ Guard();\ if (pOtherArray)\ {\ auto& otherArray = *pOtherArray;\ array.slices(get<1>(regIdx), get<2>(regIdx)) Op otherArray;\ }\ else if (value->IsNumber())\ {\ double v = value->NumberValue();\ if (floor(v) == v)\ {\ array.slices(get<1>(regIdx), get<2>(regIdx)) Op value->Int32Value();\ }\ else if (isDouble)\ {\ array.slices(get<1>(regIdx), get<2>(regIdx)) Op v;\ }\ else\ {\ array.slices(get<1>(regIdx), get<2>(regIdx)) Op (float)v;\ }\ }\ else if (value->IsObject())\ {\ if (isDouble)\ {\ auto v = ToDComplex(value);\ array.slices(get<1>(regIdx), get<2>(regIdx)) Op v;\ }\ else\ {\ auto v = ToFComplex(value);\ array.slices(get<1>(regIdx), get<2>(regIdx)) Op v;\ }\ }\ else if (value->IsString())\ {\ String::Utf8Value str(value);\ long long v = strtoll(*str, nullptr, 10);\ array.slices(get<1>(regIdx), get<2>(regIdx)) Op v;\ }\ else\ {\ return NAN_THROW_INVALID_ARGS();\ }\ }\ break;\ }\ }\ else if (args.Length() == 3)\ {\ auto idx0 = ToIndex(args[0]);\ auto idx1 = ToIndex(args[1]);\ auto value = args[2];\ auto pOtherArray = TryGetArray(value);\ Guard();\ if (pOtherArray)\ {\ auto& otherArray = *pOtherArray;\ array(idx0, idx1) Op otherArray;\ }\ else if (value->IsNumber())\ {\ double v = value->NumberValue();\ if (floor(v) == v)\ {\ array(idx0, idx1) Op value->Int32Value();\ }\ else if (isDouble)\ {\ array(idx0, idx1) Op v;\ }\ else\ {\ array(idx0, idx1) Op (float)v;\ }\ }\ else if (value->IsObject())\ {\ if (isDouble)\ {\ auto v = ToDComplex(value);\ array(idx0, idx1) Op v;\ }\ else\ {\ auto v = ToFComplex(value);\ array(idx0, idx1) Op v;\ }\ }\ else if (value->IsString())\ {\ String::Utf8Value str(value);\ long long v = strtoll(*str, nullptr, 10);\ array(idx0, idx1) Op v;\ }\ else\ {\ return NAN_THROW_INVALID_ARGS();\ }\ }\ else if (args.Length() == 4)\ {\ auto idx0 = ToIndex(args[0]);\ auto idx1 = ToIndex(args[1]);\ auto idx2 = ToIndex(args[2]);\ auto value = args[3];\ auto pOtherArray = TryGetArray(value);\ Guard();\ if (pOtherArray)\ {\ auto& otherArray = *pOtherArray;\ array(idx0, idx1, idx2) Op otherArray;\ }\ else if (value->IsNumber())\ {\ double v = value->NumberValue();\ if (floor(v) == v)\ {\ array(idx0, idx1, idx2) Op value->Int32Value();\ }\ else if (isDouble)\ {\ array(idx0, idx1, idx2) Op v;\ }\ else\ {\ array(idx0, idx1, idx2) Op (float)v;\ }\ }\ else if (value->IsObject())\ {\ if (isDouble)\ {\ auto v = ToDComplex(value);\ array(idx0, idx1, idx2) Op v;\ }\ else\ {\ auto v = ToFComplex(value);\ array(idx0, idx1, idx2) Op v;\ }\ }\ else if (value->IsString())\ {\ String::Utf8Value str(value);\ long long v = strtoll(*str, nullptr, 10);\ array(idx0, idx1, idx2) Op v;\ }\ else\ {\ return NAN_THROW_INVALID_ARGS();\ }\ }\ else\ {\ auto idx0 = ToIndex(args[0]);\ auto idx1 = ToIndex(args[1]);\ auto idx2 = ToIndex(args[2]);\ auto idx3 = ToIndex(args[3]);\ auto value = args[4];\ auto pOtherArray = TryGetArray(value);\ Guard();\ if (pOtherArray)\ {\ auto& otherArray = *pOtherArray;\ array(idx0, idx1, idx2, idx3) Op otherArray;\ }\ else if (value->IsNumber())\ {\ double v = value->NumberValue();\ if (floor(v) == v)\ {\ array(idx0, idx1, idx2, idx3) Op value->Int32Value();\ }\ else if (isDouble)\ {\ array(idx0, idx1, idx2, idx3) Op v;\ }\ else\ {\ array(idx0, idx1, idx2, idx3) Op (float)v;\ }\ }\ else if (value->IsObject())\ {\ if (isDouble)\ {\ auto v = ToDComplex(value);\ array(idx0, idx1, idx2, idx3) Op v;\ }\ else\ {\ auto v = ToFComplex(value);\ array(idx0, idx1, idx2, idx3) Op v;\ }\ }\ else if (value->IsString())\ {\ String::Utf8Value str(value);\ long long v = strtoll(*str, nullptr, 10);\ array(idx0, idx1, idx2, idx3) Op v;\ }\ else\ {\ return NAN_THROW_INVALID_ARGS();\ }\ }\ \ NanReturnValue(args.This());\ }\ ARRAYFIRE_CATCH\ } AFARRAY_IMPL_ASSIGN(Assign, =) AFARRAY_IMPL_ASSIGN(AddAssign, +=) AFARRAY_IMPL_ASSIGN(SubAssign, -=) AFARRAY_IMPL_ASSIGN(MulAssign, *=) AFARRAY_IMPL_ASSIGN(DivAssign, /=) #undef AFARRAY_IMPL_ASSIGN #define AFARRAY_IMPL_BINOP(F, Op)\ NAN_METHOD(ArrayWrapper::F)\ {\ NanScope();\ \ try\ {\ auto& array = *GetArray(args.This());\ bool isDouble = NeedsDouble(array);\ ARGS_LEN(1)\ auto value = args[0];\ auto pOtherArray = TryGetArray(value);\ af::array* result = nullptr;\ Guard();\ if (pOtherArray)\ {\ auto& otherArray = *pOtherArray;\ result = new af::array(array Op otherArray);\ }\ else if (value->IsNumber())\ {\ double v = value->NumberValue();\ if (floor(v) == v)\ {\ result = new af::array(array Op value->Int32Value());\ }\ else if (isDouble)\ {\ result = new af::array(array Op v);\ }\ else\ {\ result = new af::array(array Op (float)v);\ }\ }\ else if (value->IsObject())\ {\ if (isDouble)\ {\ auto v = ToDComplex(value);\ result = new af::array(array Op v);\ }\ else\ {\ auto v = ToFComplex(value);\ result = new af::array(array Op v);\ }\ }\ else if (value->IsString())\ {\ String::Utf8Value str(value);\ intl v = strtoll(*str, nullptr, 10);\ result = new af::array(array Op v);\ }\ else\ {\ return NAN_THROW_INVALID_ARGS();\ }\ \ NanReturnValue(New(result));\ }\ ARRAYFIRE_CATCH\ } AFARRAY_IMPL_BINOP(Add, +) AFARRAY_IMPL_BINOP(Sub, -) AFARRAY_IMPL_BINOP(Mul, *) AFARRAY_IMPL_BINOP(Div, /) AFARRAY_IMPL_BINOP(BitShiftL, <<) AFARRAY_IMPL_BINOP(BitShiftR, >>) AFARRAY_IMPL_BINOP(Lt, <) AFARRAY_IMPL_BINOP(Gt, >) AFARRAY_IMPL_BINOP(Le, <=) AFARRAY_IMPL_BINOP(Ge, >=) AFARRAY_IMPL_BINOP(Eq, ==) AFARRAY_IMPL_BINOP(Neq, !=) AFARRAY_IMPL_BINOP(And, &&) AFARRAY_IMPL_BINOP(Or, ||) AFARRAY_IMPL_BINOP(BitAnd, &) AFARRAY_IMPL_BINOP(BitOr, |) AFARRAY_IMPL_BINOP(BitXor, ^) #undef AFARRAY_IMPL_BINOP #define AFARRAY_IMPL_BINOP(F, Op)\ NAN_METHOD(ArrayWrapper::F)\ {\ NanScope();\ \ try\ {\ auto& array = *GetArray(args.This());\ bool isDouble = NeedsDouble(array);\ ARGS_LEN(1)\ auto value = args[0];\ auto pOtherArray = TryGetArray(value);\ af::array* result = nullptr;\ Guard();\ if (pOtherArray)\ {\ auto& otherArray = *pOtherArray;\ result = new af::array(otherArray Op array);\ }\ else if (value->IsNumber())\ {\ double v = value->NumberValue();\ if (floor(v) == v)\ {\ result = new af::array(value->Int32Value() Op array);\ }\ else if (isDouble)\ {\ result = new af::array(v Op array);\ }\ else\ {\ result = new af::array((float)v Op array);\ }\ }\ else if (value->IsObject())\ {\ if (isDouble)\ {\ auto v = ToDComplex(value);\ result = new af::array(v Op array);\ }\ else\ {\ auto v = ToFComplex(value);\ result = new af::array(v Op array);\ }\ }\ else if (value->IsString())\ {\ String::Utf8Value str(value);\ intl v = strtoll(*str, nullptr, 10);\ result = new af::array(v Op array);\ }\ else\ {\ return NAN_THROW_INVALID_ARGS();\ }\ \ NanReturnValue(New(result));\ }\ ARRAYFIRE_CATCH\ } AFARRAY_IMPL_BINOP(RhsAdd, +) AFARRAY_IMPL_BINOP(RhsSub, -) AFARRAY_IMPL_BINOP(RhsMul, *) AFARRAY_IMPL_BINOP(RhsDiv, /) AFARRAY_IMPL_BINOP(RhsBitShiftL, <<) AFARRAY_IMPL_BINOP(RhsBitShiftR, >>) AFARRAY_IMPL_BINOP(RhsLt, <) AFARRAY_IMPL_BINOP(RhsGt, >) AFARRAY_IMPL_BINOP(RhsLe, <=) AFARRAY_IMPL_BINOP(RhsGe, >=) AFARRAY_IMPL_BINOP(RhsEq, ==) AFARRAY_IMPL_BINOP(RhsNeq, !=) AFARRAY_IMPL_BINOP(RhsAnd, &&) AFARRAY_IMPL_BINOP(RhsOr, ||) AFARRAY_IMPL_BINOP(RhsBitAnd, &) AFARRAY_IMPL_BINOP(RhsBitOr, |) AFARRAY_IMPL_BINOP(RhsBitXor, ^) #undef AFARRAY_IMPL_BINOP #define AFARRAY_IMPL_UNOP(F, Op)\ NAN_METHOD(ArrayWrapper::F)\ {\ NanScope();\ \ try\ {\ auto& array = *GetArray(args.This());\ Guard();\ NanReturnValue(New(array.operator Op()));\ }\ ARRAYFIRE_CATCH\ } AFARRAY_IMPL_UNOP(Neg, -) AFARRAY_IMPL_UNOP(Not, !) #undef AFARRAY_IMPL_UNOP