diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/JavaScriptCore/runtime/AtomicsObject.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/JavaScriptCore/runtime/AtomicsObject.cpp')
-rw-r--r-- | Source/JavaScriptCore/runtime/AtomicsObject.cpp | 396 |
1 files changed, 396 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/runtime/AtomicsObject.cpp b/Source/JavaScriptCore/runtime/AtomicsObject.cpp new file mode 100644 index 000000000..9cd2427aa --- /dev/null +++ b/Source/JavaScriptCore/runtime/AtomicsObject.cpp @@ -0,0 +1,396 @@ +/* + * Copyright (C) 2016-2017 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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 "config.h" +#include "AtomicsObject.h" + +#include "JSCInlines.h" +#include "JSTypedArrays.h" +#include "ObjectPrototype.h" +#include "ReleaseHeapAccessScope.h" +#include "TypedArrayController.h" + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(AtomicsObject); + +#define FOR_EACH_ATOMICS_FUNC(macro) \ + macro(add, Add, 3) \ + macro(and, And, 3) \ + macro(compareExchange, CompareExchange, 4) \ + macro(exchange, Exchange, 3) \ + macro(isLockFree, IsLockFree, 1) \ + macro(load, Load, 2) \ + macro(or, Or, 3) \ + macro(store, Store, 3) \ + macro(sub, Sub, 3) \ + macro(wait, Wait, 4) \ + macro(wake, Wake, 3) \ + macro(xor, Xor, 3) + +#define DECLARE_FUNC_PROTO(lowerName, upperName, count) \ + EncodedJSValue JSC_HOST_CALL atomicsFunc ## upperName(ExecState*); +FOR_EACH_ATOMICS_FUNC(DECLARE_FUNC_PROTO) +#undef DECLARE_FUNC_PROTO + +const ClassInfo AtomicsObject::s_info = { "Atomics", &Base::s_info, 0, CREATE_METHOD_TABLE(AtomicsObject) }; + +AtomicsObject::AtomicsObject(VM& vm, Structure* structure) + : JSNonFinalObject(vm, structure) +{ +} + +AtomicsObject* AtomicsObject::create(VM& vm, JSGlobalObject* globalObject, Structure* structure) +{ + AtomicsObject* object = new (NotNull, allocateCell<AtomicsObject>(vm.heap)) AtomicsObject(vm, structure); + object->finishCreation(vm, globalObject); + return object; +} + +Structure* AtomicsObject::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +void AtomicsObject::finishCreation(VM& vm, JSGlobalObject* globalObject) +{ + Base::finishCreation(vm); + ASSERT(inherits(vm, info())); + +#define PUT_DIRECT_NATIVE_FUNC(lowerName, upperName, count) \ + putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, #lowerName), count, atomicsFunc ## upperName, Atomics ## upperName ## Intrinsic, DontEnum); + FOR_EACH_ATOMICS_FUNC(PUT_DIRECT_NATIVE_FUNC) +#undef PUT_DIRECT_NATIVE_FUNC +} + +namespace { + +template<unsigned numExtraArgs, typename Adaptor, typename Func> +EncodedJSValue atomicOperationWithArgsCase(ExecState* exec, ThrowScope& scope, JSArrayBufferView* typedArrayView, unsigned accessIndex, const Func& func) +{ + JSGenericTypedArrayView<Adaptor>* typedArray = jsCast<JSGenericTypedArrayView<Adaptor>*>(typedArrayView); + + double extraArgs[numExtraArgs + 1]; // Add 1 to avoid 0 size array error in VS. + for (unsigned i = 0; i < numExtraArgs; ++i) { + double value = exec->argument(2 + i).toInteger(exec); + RETURN_IF_EXCEPTION(scope, JSValue::encode(jsUndefined())); + extraArgs[i] = value; + } + + return JSValue::encode(func(typedArray->typedVector() + accessIndex, extraArgs)); +} + +unsigned validatedAccessIndex(VM& vm, ExecState* exec, JSArrayBufferView* typedArrayView) +{ + auto scope = DECLARE_THROW_SCOPE(vm); + JSValue accessIndexValue = exec->argument(1); + if (UNLIKELY(!accessIndexValue.isInt32())) { + double accessIndexDouble = accessIndexValue.toNumber(exec); + RETURN_IF_EXCEPTION(scope, 0); + if (accessIndexDouble == 0) + accessIndexValue = jsNumber(0); + else { + accessIndexValue = jsNumber(accessIndexDouble); + if (!accessIndexValue.isInt32()) { + throwRangeError(exec, scope, ASCIILiteral("Access index is not an integer.")); + return 0; + } + } + } + int32_t accessIndex = accessIndexValue.asInt32(); + + ASSERT(typedArrayView->length() <= static_cast<unsigned>(INT_MAX)); + if (static_cast<unsigned>(accessIndex) >= typedArrayView->length()) { + throwRangeError(exec, scope, ASCIILiteral("Access index out of bounds for atomic access.")); + return 0; + } + + return accessIndex; +} + +template<unsigned numExtraArgs, typename Func> +EncodedJSValue atomicOperationWithArgs(ExecState* exec, const Func& func) +{ + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSValue typedArrayValue = exec->argument(0); + if (!typedArrayValue.isCell()) { + throwTypeError(exec, scope, ASCIILiteral("Typed array argument must be a cell.")); + return JSValue::encode(jsUndefined()); + } + + JSCell* typedArrayCell = typedArrayValue.asCell(); + + JSType type = typedArrayCell->type(); + switch (type) { + case Int8ArrayType: + case Int16ArrayType: + case Int32ArrayType: + case Uint8ArrayType: + case Uint16ArrayType: + case Uint32ArrayType: + break; + default: + throwTypeError(exec, scope, ASCIILiteral("Typed array argument must be an Int8Array, Int16Array, Int32Array, Uint8Array, Uint16Array, or Uint32Array.")); + return JSValue::encode(jsUndefined()); + } + + JSArrayBufferView* typedArrayView = jsCast<JSArrayBufferView*>(typedArrayCell); + if (!typedArrayView->isShared()) { + throwTypeError(exec, scope, ASCIILiteral("Typed array argument must wrap a SharedArrayBuffer.")); + return JSValue::encode(jsUndefined()); + } + + unsigned accessIndex = validatedAccessIndex(vm, exec, typedArrayView); + RETURN_IF_EXCEPTION(scope, JSValue::encode(jsUndefined())); + + switch (type) { + case Int8ArrayType: + return atomicOperationWithArgsCase<numExtraArgs, Int8Adaptor>(exec, scope, typedArrayView, accessIndex, func); + case Int16ArrayType: + return atomicOperationWithArgsCase<numExtraArgs, Int16Adaptor>(exec, scope, typedArrayView, accessIndex, func); + case Int32ArrayType: + return atomicOperationWithArgsCase<numExtraArgs, Int32Adaptor>(exec, scope, typedArrayView, accessIndex, func); + case Uint8ArrayType: + return atomicOperationWithArgsCase<numExtraArgs, Uint8Adaptor>(exec, scope, typedArrayView, accessIndex, func); + case Uint16ArrayType: + return atomicOperationWithArgsCase<numExtraArgs, Uint16Adaptor>(exec, scope, typedArrayView, accessIndex, func); + case Uint32ArrayType: + return atomicOperationWithArgsCase<numExtraArgs, Uint32Adaptor>(exec, scope, typedArrayView, accessIndex, func); + default: + RELEASE_ASSERT_NOT_REACHED(); + return JSValue::encode(jsUndefined()); + } +} + +} // anonymous namespace + +EncodedJSValue JSC_HOST_CALL atomicsFuncAdd(ExecState* exec) +{ + return atomicOperationWithArgs<1>( + exec, [&] (auto* ptr, const double* args) { + return jsNumber(WTF::atomicExchangeAdd(ptr, toInt32(args[0]))); + }); +} + +EncodedJSValue JSC_HOST_CALL atomicsFuncAnd(ExecState* exec) +{ + return atomicOperationWithArgs<1>( + exec, [&] (auto* ptr, const double* args) { + return jsNumber(WTF::atomicExchangeAnd(ptr, toInt32(args[0]))); + }); +} + +EncodedJSValue JSC_HOST_CALL atomicsFuncCompareExchange(ExecState* exec) +{ + return atomicOperationWithArgs<2>( + exec, [&] (auto* ptr, const double* args) { + typedef typename std::remove_pointer<decltype(ptr)>::type T; + T expected = static_cast<T>(toInt32(args[0])); + T newValue = static_cast<T>(toInt32(args[1])); + return jsNumber(WTF::atomicCompareExchangeStrong(ptr, expected, newValue)); + }); +} + +EncodedJSValue JSC_HOST_CALL atomicsFuncExchange(ExecState* exec) +{ + return atomicOperationWithArgs<1>( + exec, [&] (auto* ptr, const double* args) { + typedef typename std::remove_pointer<decltype(ptr)>::type T; + return jsNumber(WTF::atomicExchange(ptr, static_cast<T>(toInt32(args[0])))); + }); +} + +EncodedJSValue JSC_HOST_CALL atomicsFuncIsLockFree(ExecState* exec) +{ + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + int32_t size = exec->argument(0).toInt32(exec); + RETURN_IF_EXCEPTION(scope, JSValue::encode(jsUndefined())); + + bool result; + switch (size) { + case 1: + case 2: + case 4: + result = true; + break; + default: + result = false; + break; + } + return JSValue::encode(jsBoolean(result)); +} + +EncodedJSValue JSC_HOST_CALL atomicsFuncLoad(ExecState* exec) +{ + return atomicOperationWithArgs<0>( + exec, [&] (auto* ptr, const double*) { + return jsNumber(WTF::atomicLoad(ptr)); + }); +} + +EncodedJSValue JSC_HOST_CALL atomicsFuncOr(ExecState* exec) +{ + return atomicOperationWithArgs<1>( + exec, [&] (auto* ptr, const double* args) { + return jsNumber(WTF::atomicExchangeOr(ptr, toInt32(args[0]))); + }); +} + +EncodedJSValue JSC_HOST_CALL atomicsFuncStore(ExecState* exec) +{ + return atomicOperationWithArgs<1>( + exec, [&] (auto* ptr, const double* args) { + typedef typename std::remove_pointer<decltype(ptr)>::type T; + double valueAsInt = args[0]; + T valueAsT = static_cast<T>(toInt32(valueAsInt)); + WTF::atomicStore(ptr, valueAsT); + return jsNumber(valueAsInt); + }); +} + +EncodedJSValue JSC_HOST_CALL atomicsFuncSub(ExecState* exec) +{ + return atomicOperationWithArgs<1>( + exec, [&] (auto* ptr, const double* args) { + return jsNumber(WTF::atomicExchangeSub(ptr, toInt32(args[0]))); + }); +} + +EncodedJSValue JSC_HOST_CALL atomicsFuncWait(ExecState* exec) +{ + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSInt32Array* typedArray = jsDynamicCast<JSInt32Array*>(vm, exec->argument(0)); + if (!typedArray) { + throwTypeError(exec, scope, ASCIILiteral("Typed array for wait/wake must be an Int32Array.")); + return JSValue::encode(jsUndefined()); + } + + if (!typedArray->isShared()) { + throwTypeError(exec, scope, ASCIILiteral("Typed array for wait/wake must wrap a SharedArrayBuffer.")); + return JSValue::encode(jsUndefined()); + } + + unsigned accessIndex = validatedAccessIndex(vm, exec, typedArray); + RETURN_IF_EXCEPTION(scope, JSValue::encode(jsUndefined())); + + int32_t* ptr = typedArray->typedVector() + accessIndex; + + int32_t expectedValue = exec->argument(2).toInt32(exec); + RETURN_IF_EXCEPTION(scope, JSValue::encode(jsUndefined())); + + double timeoutInMilliseconds = exec->argument(3).toNumber(exec); + RETURN_IF_EXCEPTION(scope, JSValue::encode(jsUndefined())); + + if (!vm.m_typedArrayController->isAtomicsWaitAllowedOnCurrentThread()) { + throwTypeError(exec, scope, ASCIILiteral("Atomics.wait cannot be called from the current thread.")); + return JSValue::encode(jsUndefined()); + } + + Seconds timeout = Seconds::fromMilliseconds(timeoutInMilliseconds); + + // This covers the proposed rule: + // + // 4. If timeout is not provided or is undefined then let t be +inf. Otherwise: + // a. Let q be ? ToNumber(timeout). + // b. If q is NaN then let t be +inf, otherwise let t be max(0, q). + // + // exec->argument(3) returns undefined if it's not provided and ToNumber(undefined) returns NaN, + // so NaN is the only special case. + if (timeout == timeout) + timeout = std::max(0_s, timeout); + else + timeout = Seconds::infinity(); + + bool didPassValidation = false; + ParkingLot::ParkResult result; + { + ReleaseHeapAccessScope releaseHeapAccessScope(vm.heap); + result = ParkingLot::parkConditionally( + ptr, + [&] () -> bool { + didPassValidation = WTF::atomicLoad(ptr) == expectedValue; + return didPassValidation; + }, + [] () { }, + MonotonicTime::now() + timeout); + } + const char* resultString; + if (!didPassValidation) + resultString = "not-equal"; + else if (!result.wasUnparked) + resultString = "timed-out"; + else + resultString = "ok"; + return JSValue::encode(jsString(exec, ASCIILiteral(resultString))); +} + +EncodedJSValue JSC_HOST_CALL atomicsFuncWake(ExecState* exec) +{ + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSInt32Array* typedArray = jsDynamicCast<JSInt32Array*>(vm, exec->argument(0)); + if (!typedArray) { + throwTypeError(exec, scope, ASCIILiteral("Typed array for wait/wake must be an Int32Array.")); + return JSValue::encode(jsUndefined()); + } + + if (!typedArray->isShared()) { + throwTypeError(exec, scope, ASCIILiteral("Typed array for wait/wake must wrap a SharedArrayBuffer.")); + return JSValue::encode(jsUndefined()); + } + + unsigned accessIndex = validatedAccessIndex(vm, exec, typedArray); + RETURN_IF_EXCEPTION(scope, JSValue::encode(jsUndefined())); + + int32_t* ptr = typedArray->typedVector() + accessIndex; + + JSValue countValue = exec->argument(2); + unsigned count = UINT_MAX; + if (!countValue.isUndefined()) { + int32_t countInt = countValue.toInt32(exec); + RETURN_IF_EXCEPTION(scope, JSValue::encode(jsUndefined())); + count = std::max(0, countInt); + } + + return JSValue::encode(jsNumber(ParkingLot::unparkCount(ptr, count))); +} + +EncodedJSValue JSC_HOST_CALL atomicsFuncXor(ExecState* exec) +{ + return atomicOperationWithArgs<1>( + exec, [&] (auto* ptr, const double* args) { + return jsNumber(WTF::atomicExchangeXor(ptr, toInt32(args[0]))); + }); +} + +} // namespace JSC + |