bool js::atomics_load(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); HandleValue objv = args.get(0); HandleValue idxv = args.get(1); MutableHandleValue r = args.rval(); Rooted<SharedTypedArrayObject*> view(cx, nullptr); if (!GetSharedTypedArray(cx, objv, &view)) return false; uint32_t offset; bool inRange; if (!GetSharedTypedArrayIndex(cx, idxv, view, &offset, &inRange)) return false; if (!inRange) return atomics_fence_impl(cx, r); switch (view->type()) { case Scalar::Uint8: case Scalar::Uint8Clamped: { uint8_t v = jit::AtomicOperations::loadSeqCst((uint8_t*)view->viewData() + offset); r.setInt32(v); return true; } case Scalar::Int8: { int8_t v = jit::AtomicOperations::loadSeqCst((uint8_t*)view->viewData() + offset); r.setInt32(v); return true; } case Scalar::Int16: { int16_t v = jit::AtomicOperations::loadSeqCst((int16_t*)view->viewData() + offset); r.setInt32(v); return true; } case Scalar::Uint16: { uint16_t v = jit::AtomicOperations::loadSeqCst((uint16_t*)view->viewData() + offset); r.setInt32(v); return true; } case Scalar::Int32: { int32_t v = jit::AtomicOperations::loadSeqCst((int32_t*)view->viewData() + offset); r.setInt32(v); return true; } case Scalar::Uint32: { uint32_t v = jit::AtomicOperations::loadSeqCst((uint32_t*)view->viewData() + offset); r.setNumber(v); return true; } default: return ReportBadArrayType(cx); } }
static bool AtomicsBinop(JSContext* cx, HandleValue objv, HandleValue idxv, HandleValue valv, MutableHandleValue r) { Rooted<TypedArrayObject*> view(cx, nullptr); if (!GetSharedTypedArray(cx, objv, &view)) return false; uint32_t offset; if (!GetTypedArrayIndex(cx, idxv, view, &offset)) return false; int32_t numberValue; if (!ToInt32(cx, valv, &numberValue)) return false; SharedMem<void*> viewData = view->viewDataShared(); switch (view->type()) { case Scalar::Int8: { int8_t v = (int8_t)numberValue; r.setInt32(T::operate(viewData.cast<int8_t*>() + offset, v)); return true; } case Scalar::Uint8: { uint8_t v = (uint8_t)numberValue; r.setInt32(T::operate(viewData.cast<uint8_t*>() + offset, v)); return true; } case Scalar::Int16: { int16_t v = (int16_t)numberValue; r.setInt32(T::operate(viewData.cast<int16_t*>() + offset, v)); return true; } case Scalar::Uint16: { uint16_t v = (uint16_t)numberValue; r.setInt32(T::operate(viewData.cast<uint16_t*>() + offset, v)); return true; } case Scalar::Int32: { int32_t v = numberValue; r.setInt32(T::operate(viewData.cast<int32_t*>() + offset, v)); return true; } case Scalar::Uint32: { uint32_t v = (uint32_t)numberValue; r.setNumber((double)T::operate(viewData.cast<uint32_t*>() + offset, v)); return true; } default: return ReportBadArrayType(cx); } }
static JSBool ArgGetter(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp) { if (!obj->isNormalArguments()) return true; NormalArgumentsObject &argsobj = obj->asNormalArguments(); if (JSID_IS_INT(id)) { /* * arg can exceed the number of arguments if a script changed the * prototype to point to another Arguments object with a bigger argc. */ unsigned arg = unsigned(JSID_TO_INT(id)); if (arg < argsobj.initialLength() && !argsobj.isElementDeleted(arg)) vp.set(argsobj.element(arg)); } else if (JSID_IS_ATOM(id, cx->names().length)) { if (!argsobj.hasOverriddenLength()) vp.setInt32(argsobj.initialLength()); } else { JS_ASSERT(JSID_IS_ATOM(id, cx->names().callee)); if (!argsobj.callee().isMagic(JS_OVERWRITTEN_CALLEE)) vp.set(argsobj.callee()); } return true; }
static bool MappedArgGetter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp) { MappedArgumentsObject& argsobj = obj->as<MappedArgumentsObject>(); if (JSID_IS_INT(id)) { /* * arg can exceed the number of arguments if a script changed the * prototype to point to another Arguments object with a bigger argc. */ unsigned arg = unsigned(JSID_TO_INT(id)); if (arg < argsobj.initialLength() && !argsobj.isElementDeleted(arg)) vp.set(argsobj.element(arg)); } else if (JSID_IS_ATOM(id, cx->names().length)) { if (!argsobj.hasOverriddenLength()) vp.setInt32(argsobj.initialLength()); } else { MOZ_ASSERT(JSID_IS_ATOM(id, cx->names().callee)); if (!argsobj.hasOverriddenCallee()) { RootedFunction callee(cx, &argsobj.callee()); if (callee->isAsync()) vp.setObject(*GetWrappedAsyncFunction(callee)); else vp.setObject(*callee); } } return true; }
static bool ExchangeOrStore(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); HandleValue objv = args.get(0); HandleValue idxv = args.get(1); HandleValue valv = args.get(2); MutableHandleValue r = args.rval(); Rooted<TypedArrayObject*> view(cx, nullptr); if (!GetSharedTypedArray(cx, objv, &view)) return false; uint32_t offset; if (!GetTypedArrayIndex(cx, idxv, view, &offset)) return false; double integerValue; if (!ToInteger(cx, valv, &integerValue)) return false; bool badType = false; int32_t result = ExchangeOrStore<op>(view->type(), JS::ToInt32(integerValue), view->viewDataShared(), offset, &badType); if (badType) return ReportBadArrayType(cx); if (op == DoStore) r.setNumber(integerValue); else if (view->type() == Scalar::Uint32) r.setNumber((double)(uint32_t)result); else r.setInt32(result); return true; }
bool Compare(JSContext* cx, HandleString src1, HandleString src2, MutableHandleValue rval) { nsresult rv; if (!mCollation) { nsCOMPtr<nsILocaleService> localeService = do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv); if (NS_SUCCEEDED(rv)) { nsCOMPtr<nsILocale> locale; rv = localeService->GetApplicationLocale(getter_AddRefs(locale)); if (NS_SUCCEEDED(rv)) { nsCOMPtr<nsICollationFactory> colFactory = do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID, &rv); if (NS_SUCCEEDED(rv)) { rv = colFactory->CreateCollation(locale, getter_AddRefs(mCollation)); } } } if (NS_FAILED(rv)) { xpc::Throw(cx, rv); return false; } } nsAutoJSString autoStr1, autoStr2; if (!autoStr1.init(cx, src1) || !autoStr2.init(cx, src2)) { return false; } int32_t result; rv = mCollation->CompareString(nsICollation::kCollationStrengthDefault, autoStr1, autoStr2, &result); if (NS_FAILED(rv)) { xpc::Throw(cx, rv); return false; } rval.setInt32(result); return true; }
static bool UnmappedArgGetter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp) { UnmappedArgumentsObject& argsobj = obj->as<UnmappedArgumentsObject>(); if (JSID_IS_INT(id)) { /* * arg can exceed the number of arguments if a script changed the * prototype to point to another Arguments object with a bigger argc. */ unsigned arg = unsigned(JSID_TO_INT(id)); if (arg < argsobj.initialLength() && !argsobj.isElementDeleted(arg)) vp.set(argsobj.element(arg)); } else { MOZ_ASSERT(JSID_IS_ATOM(id, cx->names().length)); if (!argsobj.hasOverriddenLength()) vp.setInt32(argsobj.initialLength()); } return true; }
bool js::atomics_compareExchange(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); HandleValue objv = args.get(0); HandleValue idxv = args.get(1); HandleValue oldv = args.get(2); HandleValue newv = args.get(3); MutableHandleValue r = args.rval(); Rooted<SharedTypedArrayObject*> view(cx, nullptr); if (!GetSharedTypedArray(cx, objv, &view)) return false; uint32_t offset; bool inRange; if (!GetSharedTypedArrayIndex(cx, idxv, view, &offset, &inRange)) return false; int32_t oldCandidate; if (!ToInt32(cx, oldv, &oldCandidate)) return false; int32_t newCandidate; if (!ToInt32(cx, newv, &newCandidate)) return false; if (!inRange) return atomics_fence_impl(cx, r); bool badType = false; int32_t result = do_cmpxchg(view->type(), oldCandidate, newCandidate, view->viewData(), offset, &badType); if (badType) return ReportBadArrayType(cx); if (view->type() == Scalar::Uint32) r.setNumber((double)(uint32_t)result); else r.setInt32(result); return true; }
static bool ExchangeOrStore(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); HandleValue objv = args.get(0); HandleValue idxv = args.get(1); HandleValue valv = args.get(2); MutableHandleValue r = args.rval(); Rooted<SharedTypedArrayObject*> view(cx, nullptr); if (!GetSharedTypedArray(cx, objv, &view)) return false; uint32_t offset; bool inRange; if (!GetSharedTypedArrayIndex(cx, idxv, view, &offset, &inRange)) return false; int32_t numberValue; if (!ToInt32(cx, valv, &numberValue)) return false; if (!inRange) { atomics_fullMemoryBarrier(); r.set(valv); return true; } bool badType = false; int32_t result = ExchangeOrStore<op>(view->type(), numberValue, view->viewData(), offset, &badType); if (badType) return ReportBadArrayType(cx); if (view->type() == Scalar::Uint32) r.setNumber((double)(uint32_t)result); else r.setInt32(result); return true; }
static bool atomics_binop_impl(JSContext* cx, HandleValue objv, HandleValue idxv, HandleValue valv, MutableHandleValue r) { Rooted<SharedTypedArrayObject*> view(cx, nullptr); if (!GetSharedTypedArray(cx, objv, &view)) return false; uint32_t offset; bool inRange; if (!GetSharedTypedArrayIndex(cx, idxv, view, &offset, &inRange)) return false; int32_t numberValue; if (!ToInt32(cx, valv, &numberValue)) return false; if (!inRange) return atomics_fence_impl(cx, r); switch (view->type()) { case Scalar::Int8: { int8_t v = (int8_t)numberValue; r.setInt32(T::operate((int8_t*)view->viewData() + offset, v)); return true; } case Scalar::Uint8: { uint8_t v = (uint8_t)numberValue; r.setInt32(T::operate((uint8_t*)view->viewData() + offset, v)); return true; } case Scalar::Uint8Clamped: { // Spec says: // - clamp the input value // - perform the operation // - clamp the result // - store the result // This requires a CAS loop. int32_t value = ClampIntForUint8Array(numberValue); uint8_t* loc = (uint8_t*)view->viewData() + offset; for (;;) { uint8_t old = *loc; uint8_t result = (uint8_t)ClampIntForUint8Array(T::perform(old, value)); uint8_t tmp = jit::AtomicOperations::compareExchangeSeqCst(loc, old, result); if (tmp == old) { r.setInt32(old); break; } } return true; } case Scalar::Int16: { int16_t v = (int16_t)numberValue; r.setInt32(T::operate((int16_t*)view->viewData() + offset, v)); return true; } case Scalar::Uint16: { uint16_t v = (uint16_t)numberValue; r.setInt32(T::operate((uint16_t*)view->viewData() + offset, v)); return true; } case Scalar::Int32: { int32_t v = numberValue; r.setInt32(T::operate((int32_t*)view->viewData() + offset, v)); return true; } case Scalar::Uint32: { uint32_t v = (uint32_t)numberValue; r.setNumber((double)T::operate((uint32_t*)view->viewData() + offset, v)); return true; } default: return ReportBadArrayType(cx); } }
bool js::atomics_store(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); HandleValue objv = args.get(0); HandleValue idxv = args.get(1); HandleValue valv = args.get(2); MutableHandleValue r = args.rval(); Rooted<SharedTypedArrayObject*> view(cx, nullptr); if (!GetSharedTypedArray(cx, objv, &view)) return false; uint32_t offset; bool inRange; if (!GetSharedTypedArrayIndex(cx, idxv, view, &offset, &inRange)) return false; int32_t numberValue; if (!ToInt32(cx, valv, &numberValue)) return false; if (!inRange) { atomics_fullMemoryBarrier(); r.set(valv); return true; } switch (view->type()) { case Scalar::Int8: { int8_t value = (int8_t)numberValue; jit::AtomicOperations::storeSeqCst((int8_t*)view->viewData() + offset, value); r.setInt32(value); return true; } case Scalar::Uint8: { uint8_t value = (uint8_t)numberValue; jit::AtomicOperations::storeSeqCst((uint8_t*)view->viewData() + offset, value); r.setInt32(value); return true; } case Scalar::Uint8Clamped: { uint8_t value = ClampIntForUint8Array(numberValue); jit::AtomicOperations::storeSeqCst((uint8_t*)view->viewData() + offset, value); r.setInt32(value); return true; } case Scalar::Int16: { int16_t value = (int16_t)numberValue; jit::AtomicOperations::storeSeqCst((int16_t*)view->viewData() + offset, value); r.setInt32(value); return true; } case Scalar::Uint16: { uint16_t value = (uint16_t)numberValue; jit::AtomicOperations::storeSeqCst((uint16_t*)view->viewData() + offset, value); r.setInt32(value); return true; } case Scalar::Int32: { int32_t value = numberValue; jit::AtomicOperations::storeSeqCst((int32_t*)view->viewData() + offset, value); r.setInt32(value); return true; } case Scalar::Uint32: { uint32_t value = (uint32_t)numberValue; jit::AtomicOperations::storeSeqCst((uint32_t*)view->viewData() + offset, value); r.setNumber((double)value); return true; } default: return ReportBadArrayType(cx); } }
bool js::atomics_store(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); HandleValue objv = args.get(0); HandleValue idxv = args.get(1); HandleValue valv = args.get(2); MutableHandleValue r = args.rval(); Rooted<SharedTypedArrayObject *> view(cx, nullptr); if (!GetSharedTypedArray(cx, objv, &view)) return false; uint32_t offset; bool inRange; if (!GetSharedTypedArrayIndex(cx, idxv, view, &offset, &inRange)) return false; int32_t numberValue; if (!ToInt32(cx, valv, &numberValue)) return false; if (!inRange) { atomics_fullMemoryBarrier(); r.set(valv); return true; } // STORE stores value in *addr // addr must be a T*, and value should be of type T #if defined(CXX11_ATOMICS) # define STORE(T, addr, value) \ do { \ std::atomic_store(reinterpret_cast<std::atomic<T>*>(addr), (T)value); \ } while(0) #elif defined(GNU_ATOMICS) # define STORE(T, addr, value) \ do { \ __sync_synchronize(); \ *(addr) = value; \ __sync_synchronize(); \ } while(0) #else # define STORE(a, b, c) (void)0 #endif switch (view->type()) { case Scalar::Int8: { int8_t value = (int8_t)numberValue; STORE(int8_t, (int8_t*)view->viewData() + offset, value); r.setInt32(value); return true; } case Scalar::Uint8: { uint8_t value = (uint8_t)numberValue; STORE(uint8_t, (uint8_t*)view->viewData() + offset, value); r.setInt32(value); return true; } case Scalar::Uint8Clamped: { uint8_t value = ClampIntForUint8Array(numberValue); STORE(uint8_t, (uint8_t*)view->viewData() + offset, value); r.setInt32(value); return true; } case Scalar::Int16: { int16_t value = (int16_t)numberValue; STORE(int16_t, (int16_t*)view->viewData() + offset, value); r.setInt32(value); return true; } case Scalar::Uint16: { uint16_t value = (uint16_t)numberValue; STORE(uint16_t, (uint16_t*)view->viewData() + offset, value); r.setInt32(value); return true; } case Scalar::Int32: { int32_t value = numberValue; STORE(int32_t, (int32_t*)view->viewData() + offset, value); r.setInt32(value); return true; } case Scalar::Uint32: { uint32_t value = (uint32_t)numberValue; STORE(uint32_t, (uint32_t*)view->viewData() + offset, value); r.setNumber((double)value); return true; } default: return ReportBadArrayType(cx); } #undef STORE }
bool js::atomics_load(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); HandleValue objv = args.get(0); HandleValue idxv = args.get(1); MutableHandleValue r = args.rval(); Rooted<SharedTypedArrayObject *> view(cx, nullptr); if (!GetSharedTypedArray(cx, objv, &view)) return false; uint32_t offset; bool inRange; if (!GetSharedTypedArrayIndex(cx, idxv, view, &offset, &inRange)) return false; if (!inRange) return atomics_fence_impl(cx, r); // LOAD sets v to the value of *addr // addr must be a T*, and v must be a variable of type T #if defined(CXX11_ATOMICS) # define LOAD(T, addr, v) \ do { \ v = std::atomic_load(reinterpret_cast<std::atomic<T>*>(addr)); \ } while(0) #elif defined(GNU_ATOMICS) # define LOAD(T, addr, v) \ do { \ __sync_synchronize(); \ v = *(addr); \ __sync_synchronize(); \ } while(0) #else # define LOAD(a, b, v) v = 0 #endif switch (view->type()) { case Scalar::Uint8: case Scalar::Uint8Clamped: { uint8_t v; LOAD(uint8_t, (uint8_t*)view->viewData() + offset, v); r.setInt32(v); return true; } case Scalar::Int8: { int8_t v; LOAD(int8_t, (int8_t*)view->viewData() + offset, v); r.setInt32(v); return true; } case Scalar::Int16: { int16_t v; LOAD(int16_t, (int16_t*)view->viewData() + offset, v); r.setInt32(v); return true; } case Scalar::Uint16: { uint16_t v; LOAD(uint16_t, (uint16_t*)view->viewData() + offset, v); r.setInt32(v); return true; } case Scalar::Int32: { int32_t v; LOAD(int32_t, (int32_t*)view->viewData() + offset, v); r.setInt32(v); return true; } case Scalar::Uint32: { uint32_t v; LOAD(uint32_t, (uint32_t*)view->viewData() + offset, v); r.setNumber(v); return true; } default: return ReportBadArrayType(cx); } #undef LOAD }
bool js::atomics_compareExchange(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); HandleValue objv = args.get(0); HandleValue idxv = args.get(1); HandleValue oldv = args.get(2); HandleValue newv = args.get(3); MutableHandleValue r = args.rval(); Rooted<SharedTypedArrayObject *> view(cx, nullptr); if (!GetSharedTypedArray(cx, objv, &view)) return false; uint32_t offset; bool inRange; if (!GetSharedTypedArrayIndex(cx, idxv, view, &offset, &inRange)) return false; int32_t oldCandidate; if (!ToInt32(cx, oldv, &oldCandidate)) return false; int32_t newCandidate; if (!ToInt32(cx, newv, &newCandidate)) return false; if (!inRange) return atomics_fence_impl(cx, r); // CAS always sets oldval to the old value of the cell. // addr must be a T*, and oldval and newval should be variables of type T #if defined(CXX11_ATOMICS) # define CAS(T, addr, oldval, newval) \ do { \ std::atomic_compare_exchange_strong(reinterpret_cast<std::atomic<T>*>(addr), &oldval, newval); \ } while(0) #elif defined(GNU_ATOMICS) # define CAS(T, addr, oldval, newval) \ do { \ oldval = __sync_val_compare_and_swap(addr, (oldval), (newval)); \ } while(0) #else # define CAS(a, b, c, newval) (void)newval #endif switch (view->type()) { case Scalar::Int8: { int8_t oldval = (int8_t)oldCandidate; int8_t newval = (int8_t)newCandidate; CAS(int8_t, (int8_t*)view->viewData() + offset, oldval, newval); r.setInt32(oldval); return true; } case Scalar::Uint8: { uint8_t oldval = (uint8_t)oldCandidate; uint8_t newval = (uint8_t)newCandidate; CAS(uint8_t, (uint8_t*)view->viewData() + offset, oldval, newval); r.setInt32(oldval); return true; } case Scalar::Uint8Clamped: { uint8_t oldval = ClampIntForUint8Array(oldCandidate); uint8_t newval = ClampIntForUint8Array(newCandidate); CAS(uint8_t, (uint8_t*)view->viewData() + offset, oldval, newval); r.setInt32(oldval); return true; } case Scalar::Int16: { int16_t oldval = (int16_t)oldCandidate; int16_t newval = (int16_t)newCandidate; CAS(int16_t, (int16_t*)view->viewData() + offset, oldval, newval); r.setInt32(oldval); return true; } case Scalar::Uint16: { uint16_t oldval = (uint16_t)oldCandidate; uint16_t newval = (uint16_t)newCandidate; CAS(uint16_t, (uint16_t*)view->viewData() + offset, oldval, newval); r.setInt32(oldval); return true; } case Scalar::Int32: { int32_t oldval = oldCandidate; int32_t newval = newCandidate; CAS(int32_t, (int32_t*)view->viewData() + offset, oldval, newval); r.setInt32(oldval); return true; } case Scalar::Uint32: { uint32_t oldval = (uint32_t)oldCandidate; uint32_t newval = (uint32_t)newCandidate; CAS(uint32_t, (uint32_t*)view->viewData() + offset, oldval, newval); r.setNumber((double)oldval); return true; } default: return ReportBadArrayType(cx); } // Do not undef CAS, it is used later }