mirror of
https://gitee.com/openharmony/arkcompiler_runtime_core
synced 2024-12-18 03:38:52 +00:00
interop_js: Add rest parametes support
Issue: #I91IS7 Testing: CI tests passed Signed-off-by: Vyacheslav Cherkashin <cherkashin.vyacheslav@huawei.com> Signed-off-by: churkinaleksey <churkin.aleksey@huawei-partners.com>
This commit is contained in:
parent
ef583856e3
commit
68f7ccd8d2
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021-2023 Huawei Device Co., Ltd.
|
||||
* Copyright (c) 2021-2024 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
@ -200,6 +200,7 @@ public:
|
||||
|
||||
void SetAnnotations(std::vector<AnnotationData> &&annotations)
|
||||
{
|
||||
ASSERT(annotations_.empty());
|
||||
annotations_ = std::forward<std::vector<AnnotationData>>(annotations);
|
||||
}
|
||||
|
||||
|
@ -76,6 +76,12 @@ attributes:
|
||||
applicable_to:
|
||||
- function
|
||||
|
||||
- name: varargs
|
||||
type: bool
|
||||
flags: [ACC_VARARGS]
|
||||
applicable_to:
|
||||
- function
|
||||
|
||||
- name: static
|
||||
type: bool
|
||||
flags: [ACC_STATIC]
|
||||
|
@ -883,6 +883,9 @@ void Disassembler::GetMetaData(pandasm::Function *method, const panda_file::File
|
||||
SetEntityAttribute(
|
||||
method, [&methodAccessor]() { return methodAccessor.IsAbstract(); }, "noimpl");
|
||||
|
||||
SetEntityAttribute(
|
||||
method, [&methodAccessor]() { return methodAccessor.IsVarArgs(); }, "varargs");
|
||||
|
||||
SetEntityAttributeValue(
|
||||
method, [&methodAccessor]() { return methodAccessor.IsPublic(); }, "access.function", "public");
|
||||
|
||||
|
@ -68,6 +68,11 @@ public:
|
||||
return (accessFlags_ & ACC_ABSTRACT) != 0;
|
||||
}
|
||||
|
||||
bool IsVarArgs() const
|
||||
{
|
||||
return (accessFlags_ & ACC_VARARGS) != 0;
|
||||
}
|
||||
|
||||
bool IsNative() const
|
||||
{
|
||||
return (accessFlags_ & ACC_NATIVE) != 0;
|
||||
|
@ -21,24 +21,34 @@
|
||||
|
||||
namespace ark::ets::interop::js {
|
||||
|
||||
template <typename FUnwrapVal, typename FStore>
|
||||
[[nodiscard]] static ALWAYS_INLINE inline bool ConvertRefArgToEts(InteropCtx *ctx, ProtoReader &protoReader,
|
||||
FStore &storeRes, napi_value jsVal,
|
||||
FUnwrapVal &unwrapVal)
|
||||
template <typename Convertor, typename FStore>
|
||||
static ALWAYS_INLINE bool UnwrapVal(InteropCtx *ctx, napi_env env, napi_value jsVal, FStore &storeRes)
|
||||
{
|
||||
using cpptype = typename Convertor::cpptype; // NOLINT(readability-identifier-naming)
|
||||
auto res = Convertor::Unwrap(ctx, env, jsVal);
|
||||
if (UNLIKELY(!res.has_value())) {
|
||||
return false;
|
||||
}
|
||||
if constexpr (std::is_pointer_v<cpptype>) {
|
||||
storeRes(AsEtsObject(res.value())->GetCoreType());
|
||||
} else {
|
||||
storeRes(Value(res.value()).GetAsLong());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename FStore>
|
||||
[[nodiscard]] static ALWAYS_INLINE inline bool ConvertRefArgToEts(InteropCtx *ctx, Class *klass, FStore &storeRes,
|
||||
napi_value jsVal)
|
||||
{
|
||||
auto env = ctx->GetJSEnv();
|
||||
|
||||
if (IsNull(env, jsVal)) {
|
||||
storeRes(nullptr);
|
||||
return true;
|
||||
}
|
||||
auto klass = protoReader.GetClass();
|
||||
// start fastpath
|
||||
if (klass == ctx->GetJSValueClass()) {
|
||||
return unwrapVal(helpers::TypeIdentity<JSConvertJSValue>());
|
||||
return UnwrapVal<JSConvertJSValue>(ctx, env, jsVal, storeRes);
|
||||
}
|
||||
if (klass == ctx->GetStringClass()) {
|
||||
return unwrapVal(helpers::TypeIdentity<JSConvertString>());
|
||||
return UnwrapVal<JSConvertString>(ctx, env, jsVal, storeRes);
|
||||
}
|
||||
if (IsUndefined(env, jsVal)) {
|
||||
if (UNLIKELY(!klass->IsAssignableFrom(ctx->GetUndefinedClass()))) {
|
||||
@ -58,27 +68,16 @@ template <typename FUnwrapVal, typename FStore>
|
||||
}
|
||||
|
||||
template <typename FStore>
|
||||
[[nodiscard]] static ALWAYS_INLINE inline bool ConvertArgToEts(InteropCtx *ctx, ProtoReader &protoReader,
|
||||
FStore &storeRes, napi_value jsVal)
|
||||
[[nodiscard]] static ALWAYS_INLINE inline bool ConvertPrimArgToEts(InteropCtx *ctx, panda_file::Type::TypeId id,
|
||||
FStore &storeRes, napi_value jsVal)
|
||||
{
|
||||
auto env = ctx->GetJSEnv();
|
||||
|
||||
auto unwrapVal = [&ctx, &env, &jsVal, &storeRes](auto convTag) {
|
||||
using Convertor = typename decltype(convTag)::type; // convTag acts as lambda template parameter
|
||||
using cpptype = typename Convertor::cpptype; // NOLINT(readability-identifier-naming)
|
||||
auto res = Convertor::Unwrap(ctx, env, jsVal);
|
||||
if (UNLIKELY(!res.has_value())) {
|
||||
return false;
|
||||
}
|
||||
if constexpr (std::is_pointer_v<cpptype>) {
|
||||
storeRes(AsEtsObject(res.value())->GetCoreType());
|
||||
} else {
|
||||
storeRes(Value(res.value()).GetAsLong());
|
||||
}
|
||||
return true;
|
||||
return UnwrapVal<Convertor>(ctx, env, jsVal, storeRes);
|
||||
};
|
||||
|
||||
switch (protoReader.GetType().GetId()) {
|
||||
switch (id) {
|
||||
case panda_file::Type::TypeId::VOID: {
|
||||
return true; // do nothing
|
||||
}
|
||||
@ -104,8 +103,109 @@ template <typename FStore>
|
||||
return unwrapVal(helpers::TypeIdentity<JSConvertF32>());
|
||||
case panda_file::Type::TypeId::F64:
|
||||
return unwrapVal(helpers::TypeIdentity<JSConvertF64>());
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename FStore, typename GetClass>
|
||||
[[nodiscard]] static ALWAYS_INLINE inline bool ConvertArgToEts(InteropCtx *ctx, panda_file::Type type, FStore &storeRes,
|
||||
const GetClass &getClass, napi_value jsVal)
|
||||
{
|
||||
auto id = type.GetId();
|
||||
auto env = ctx->GetJSEnv();
|
||||
if (id == panda_file::Type::TypeId::REFERENCE) {
|
||||
if (IsNull(env, jsVal)) {
|
||||
storeRes(nullptr);
|
||||
return true;
|
||||
}
|
||||
return ConvertRefArgToEts(ctx, getClass(), storeRes, jsVal);
|
||||
}
|
||||
return ConvertPrimArgToEts(ctx, id, storeRes, jsVal);
|
||||
}
|
||||
|
||||
template <typename FStore>
|
||||
[[nodiscard]] static ALWAYS_INLINE inline bool ConvertArgToEts(InteropCtx *ctx, ProtoReader &protoReader,
|
||||
FStore &storeRes, napi_value jsVal)
|
||||
{
|
||||
return ConvertArgToEts(
|
||||
ctx, protoReader.GetType(), storeRes, [&protoReader]() { return protoReader.GetClass(); }, jsVal);
|
||||
}
|
||||
|
||||
template <typename RestParamsArray>
|
||||
static ObjectHeader **DoPackRestParameters(EtsCoroutine *coro, InteropCtx *ctx, ProtoReader &protoReader,
|
||||
Span<napi_value> jsargv)
|
||||
{
|
||||
const size_t numRestParams = jsargv.size();
|
||||
|
||||
RestParamsArray *objArr = [&]() {
|
||||
if constexpr (std::is_same_v<RestParamsArray, EtsObjectArray>) {
|
||||
EtsClass *etsClass = EtsClass::FromRuntimeClass(protoReader.GetClass()->GetComponentType());
|
||||
return RestParamsArray::Create(etsClass, numRestParams);
|
||||
} else {
|
||||
return RestParamsArray::Create(numRestParams);
|
||||
}
|
||||
}();
|
||||
|
||||
auto convertValue = [](auto val) -> typename RestParamsArray::ValueType {
|
||||
constexpr bool IS_VAL_PTR = std::is_pointer_v<decltype(val)> || std::is_null_pointer_v<decltype(val)>;
|
||||
constexpr bool IS_OBJ_ARR = std::is_same_v<RestParamsArray, EtsObjectArray>;
|
||||
// Clang-tidy gives false positive error.
|
||||
if constexpr (IS_OBJ_ARR) {
|
||||
if constexpr (IS_VAL_PTR) {
|
||||
return EtsObject::FromCoreType(static_cast<ObjectHeader *>(val));
|
||||
}
|
||||
}
|
||||
if constexpr (!IS_OBJ_ARR) {
|
||||
if constexpr (!IS_VAL_PTR) {
|
||||
return *reinterpret_cast<typename RestParamsArray::ValueType *>(&val);
|
||||
}
|
||||
}
|
||||
UNREACHABLE();
|
||||
};
|
||||
|
||||
VMHandle<RestParamsArray> restArgsArray(coro, objArr->GetCoreType());
|
||||
for (uint32_t restArgIdx = 0; restArgIdx < numRestParams; ++restArgIdx) {
|
||||
auto jsVal = jsargv[restArgIdx];
|
||||
auto store = [&convertValue, restArgIdx, &restArgsArray](auto val) {
|
||||
restArgsArray.GetPtr()->Set(restArgIdx, convertValue(val));
|
||||
};
|
||||
auto klass = protoReader.GetClass()->GetComponentType();
|
||||
auto klassCb = [klass]() { return klass; };
|
||||
if (UNLIKELY(!ConvertArgToEts(ctx, klass->GetType(), store, klassCb, jsVal))) {
|
||||
if (coro->HasPendingException()) {
|
||||
ctx->ForwardEtsException(coro);
|
||||
}
|
||||
ASSERT(ctx->SanityJSExceptionPending());
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return reinterpret_cast<ObjectHeader **>(restArgsArray.GetAddress());
|
||||
}
|
||||
|
||||
[[maybe_unused]] static ObjectHeader **PackRestParameters(EtsCoroutine *coro, InteropCtx *ctx, ProtoReader &protoReader,
|
||||
Span<napi_value> jsargv)
|
||||
{
|
||||
panda_file::Type restParamsItemType = protoReader.GetClass()->GetComponentType()->GetType();
|
||||
switch (restParamsItemType.GetId()) {
|
||||
case panda_file::Type::TypeId::U1:
|
||||
return DoPackRestParameters<EtsBooleanArray>(coro, ctx, protoReader, jsargv);
|
||||
case panda_file::Type::TypeId::I8:
|
||||
return DoPackRestParameters<EtsByteArray>(coro, ctx, protoReader, jsargv);
|
||||
case panda_file::Type::TypeId::I16:
|
||||
return DoPackRestParameters<EtsShortArray>(coro, ctx, protoReader, jsargv);
|
||||
case panda_file::Type::TypeId::U16:
|
||||
return DoPackRestParameters<EtsCharArray>(coro, ctx, protoReader, jsargv);
|
||||
case panda_file::Type::TypeId::I32:
|
||||
return DoPackRestParameters<EtsIntArray>(coro, ctx, protoReader, jsargv);
|
||||
case panda_file::Type::TypeId::I64:
|
||||
return DoPackRestParameters<EtsLongArray>(coro, ctx, protoReader, jsargv);
|
||||
case panda_file::Type::TypeId::F32:
|
||||
return DoPackRestParameters<EtsFloatArray>(coro, ctx, protoReader, jsargv);
|
||||
case panda_file::Type::TypeId::F64:
|
||||
return DoPackRestParameters<EtsDoubleArray>(coro, ctx, protoReader, jsargv);
|
||||
case panda_file::Type::TypeId::REFERENCE:
|
||||
return ConvertRefArgToEts(ctx, protoReader, storeRes, jsVal, unwrapVal);
|
||||
return DoPackRestParameters<EtsObjectArray>(coro, ctx, protoReader, jsargv);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
@ -45,6 +45,9 @@ public:
|
||||
private:
|
||||
template <bool IS_STATIC>
|
||||
ALWAYS_INLINE bool ConvertArgs(Span<Value> etsArgs);
|
||||
ALWAYS_INLINE ObjectHeader **ConvertRestParams(Span<napi_value> restArgs);
|
||||
|
||||
ALWAYS_INLINE bool CheckNumArgs(size_t numArgs) const;
|
||||
|
||||
template <bool IS_STATIC>
|
||||
ALWAYS_INLINE napi_value HandleImpl();
|
||||
@ -81,11 +84,12 @@ ALWAYS_INLINE inline bool CallETSHandler::ConvertArgs(Span<Value> etsArgs)
|
||||
ObjectHeader **thisObjRoot = IS_STATIC ? nullptr : createRoot(thisObj_->GetCoreType());
|
||||
|
||||
using ArgValueBox = std::variant<uint64_t, ObjectHeader **>;
|
||||
auto numArgs = jsargv_.size();
|
||||
auto const numArgs = protoReader_.GetMethod()->GetNumArgs() - !IS_STATIC;
|
||||
auto const numNonRest = numArgs - protoReader_.GetMethod()->HasVarArgs();
|
||||
auto etsBoxedArgs = ctx_->GetTempArgs<ArgValueBox>(numArgs);
|
||||
|
||||
// Convert and store in root if necessary
|
||||
for (uint32_t argIdx = 0; argIdx < numArgs; ++argIdx, protoReader_.Advance()) {
|
||||
for (uint32_t argIdx = 0; argIdx < numNonRest; ++argIdx, protoReader_.Advance()) {
|
||||
auto jsVal = jsargv_[argIdx];
|
||||
auto store = [&etsBoxedArgs, &argIdx, createRoot](auto val) {
|
||||
if constexpr (std::is_pointer_v<decltype(val)> || std::is_null_pointer_v<decltype(val)>) {
|
||||
@ -99,6 +103,11 @@ ALWAYS_INLINE inline bool CallETSHandler::ConvertArgs(Span<Value> etsArgs)
|
||||
}
|
||||
}
|
||||
|
||||
if (protoReader_.GetMethod()->HasVarArgs()) {
|
||||
const auto restIdx = numArgs - 1;
|
||||
etsBoxedArgs[restIdx] = ConvertRestParams(jsargv_.SubSpan(restIdx));
|
||||
}
|
||||
|
||||
// Unbox values
|
||||
if constexpr (!IS_STATIC) {
|
||||
etsArgs[0] = Value(*thisObjRoot);
|
||||
@ -117,6 +126,33 @@ ALWAYS_INLINE inline bool CallETSHandler::ConvertArgs(Span<Value> etsArgs)
|
||||
return true;
|
||||
}
|
||||
|
||||
ObjectHeader **CallETSHandler::ConvertRestParams(Span<napi_value> restArgs)
|
||||
{
|
||||
ASSERT(protoReader_.GetType().IsReference());
|
||||
ASSERT(protoReader_.GetClass()->IsArrayClass());
|
||||
|
||||
ObjectHeader **restParamsSlot = PackRestParameters(coro_, ctx_, protoReader_, restArgs);
|
||||
ASSERT(restParamsSlot != nullptr);
|
||||
|
||||
return restParamsSlot;
|
||||
}
|
||||
|
||||
bool CallETSHandler::CheckNumArgs(size_t numArgs) const
|
||||
{
|
||||
const auto method = protoReader_.GetMethod();
|
||||
bool const hasRestParams = method->HasVarArgs();
|
||||
ASSERT((hasRestParams && numArgs > 0) || !hasRestParams);
|
||||
|
||||
if ((hasRestParams && (numArgs - 1) > jsargv_.size()) || (!hasRestParams && numArgs != jsargv_.size())) {
|
||||
std::string msg = "CallEtsFunction: wrong argc, ets_argc=" + std::to_string(numArgs) +
|
||||
" js_argc=" + std::to_string(jsargv_.size()) + " ets_method='" +
|
||||
std::string(method->GetFullName(true)) + "'";
|
||||
InteropCtx::ThrowJSTypeError(ctx_->GetJSEnv(), msg);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <bool IS_STATIC>
|
||||
napi_value CallETSHandler::HandleImpl()
|
||||
{
|
||||
@ -128,8 +164,7 @@ napi_value CallETSHandler::HandleImpl()
|
||||
ASSERT(IS_STATIC == (thisObj_ == nullptr));
|
||||
|
||||
auto const numArgs = method->GetNumArgs() - (IS_STATIC ? 0 : 1);
|
||||
if (UNLIKELY(numArgs != jsargv_.size())) {
|
||||
InteropCtx::ThrowJSTypeError(ctx_->GetJSEnv(), std::string("CallEtsFunction: wrong argc"));
|
||||
if (UNLIKELY(!CheckNumArgs(numArgs))) {
|
||||
return ForwardException(ctx_, coro_);
|
||||
}
|
||||
|
||||
|
@ -81,6 +81,11 @@ public:
|
||||
return method_;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE const Method *GetMethod() const
|
||||
{
|
||||
return method_;
|
||||
}
|
||||
|
||||
private:
|
||||
panda_file::ShortyIterator it_ {};
|
||||
uint32_t refArgIdx_ {};
|
||||
|
@ -252,7 +252,7 @@ static napi_property_descriptor DoMakeNapiProperty(EtsFieldWrapper *wrapper)
|
||||
case panda_file::Type::TypeId::REFERENCE:
|
||||
return setupAccessors(helpers::TypeIdentity<EtsFieldAccessorREFERENCE>());
|
||||
default:
|
||||
InteropCtx::Fatal(std::string("ConvertEtsVal: unsupported typeid ") +
|
||||
InteropCtx::Fatal(std::string("DoMakeNapiProperty: unsupported typeid ") +
|
||||
panda_file::Type::GetSignatureByTypeId(type));
|
||||
}
|
||||
UNREACHABLE();
|
||||
|
@ -55,9 +55,17 @@ public:
|
||||
ALWAYS_INLINE EtsMethod *GetMethod(uint32_t parametersNum) const
|
||||
{
|
||||
if (LIKELY(parametersNum < entries_.size())) {
|
||||
return entries_[parametersNum];
|
||||
if (LIKELY(entries_[parametersNum] != nullptr)) {
|
||||
return entries_[parametersNum];
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
// Try rest params
|
||||
for (size_t params = std::min(static_cast<size_t>(parametersNum), entries_.size() - 1); params > 0; params--) {
|
||||
if (entries_[params] != nullptr && entries_[params]->GetPandaMethod()->HasVarArgs()) {
|
||||
return entries_[params];
|
||||
}
|
||||
}
|
||||
return entries_.front();
|
||||
}
|
||||
|
||||
template <typename Callback>
|
||||
@ -96,7 +104,8 @@ private:
|
||||
entries_(PandaVector<EtsMethod *>(singleMethod->GetParametersNum() + 1)),
|
||||
anyMethod_(singleMethod)
|
||||
{
|
||||
entries_[singleMethod->GetParametersNum()] = singleMethod;
|
||||
entries_[singleMethod->GetParametersNum() -
|
||||
static_cast<unsigned int>(singleMethod->GetPandaMethod()->HasVarArgs())] = singleMethod;
|
||||
}
|
||||
|
||||
EtsClass *const enclosingClass_;
|
||||
|
@ -308,32 +308,33 @@ static JSValue *JSRuntimeLoadModule(EtsString *module)
|
||||
PandaString moduleName = module->GetMutf8();
|
||||
auto [mod, func] = ResolveModuleName(moduleName);
|
||||
|
||||
NapiScope jsHandleScope(env);
|
||||
|
||||
napi_value requireFn;
|
||||
NAPI_CHECK_FATAL(napi_get_named_property(env, GetGlobal(env), func.data(), &requireFn));
|
||||
|
||||
INTEROP_FATAL_IF(GetValueType(env, requireFn) != napi_function);
|
||||
napi_value modObj;
|
||||
{
|
||||
napi_value jsName;
|
||||
NAPI_CHECK_FATAL(napi_create_string_utf8(env, mod.data(), NAPI_AUTO_LENGTH, &jsName));
|
||||
std::array<napi_value, 1> args = {jsName};
|
||||
napi_value recv;
|
||||
NAPI_CHECK_FATAL(napi_get_undefined(env, &recv));
|
||||
auto status = (napi_call_function(env, recv, requireFn, args.size(), args.data(), &modObj));
|
||||
ScopedNativeCodeThread etsNativeScope(coro);
|
||||
NapiScope jsHandleScope(env);
|
||||
|
||||
if (status == napi_pending_exception) {
|
||||
napi_value exp;
|
||||
NAPI_CHECK_FATAL(napi_get_and_clear_last_exception(env, &exp));
|
||||
NAPI_CHECK_FATAL(napi_fatal_exception(env, exp));
|
||||
INTEROP_LOG(FATAL) << "Unable to load module due to exception";
|
||||
UNREACHABLE();
|
||||
napi_value requireFn;
|
||||
NAPI_CHECK_FATAL(napi_get_named_property(env, GetGlobal(env), func.data(), &requireFn));
|
||||
|
||||
INTEROP_FATAL_IF(GetValueType(env, requireFn) != napi_function);
|
||||
{
|
||||
napi_value jsName;
|
||||
NAPI_CHECK_FATAL(napi_create_string_utf8(env, mod.data(), NAPI_AUTO_LENGTH, &jsName));
|
||||
std::array<napi_value, 1> args = {jsName};
|
||||
napi_value recv;
|
||||
NAPI_CHECK_FATAL(napi_get_undefined(env, &recv));
|
||||
auto status = (napi_call_function(env, recv, requireFn, args.size(), args.data(), &modObj));
|
||||
if (status == napi_pending_exception) {
|
||||
napi_value exp;
|
||||
NAPI_CHECK_FATAL(napi_get_and_clear_last_exception(env, &exp));
|
||||
NAPI_CHECK_FATAL(napi_fatal_exception(env, exp));
|
||||
INTEROP_LOG(FATAL) << "Unable to load module due to exception";
|
||||
UNREACHABLE();
|
||||
}
|
||||
INTEROP_FATAL_IF(status != napi_ok);
|
||||
}
|
||||
|
||||
INTEROP_FATAL_IF(status != napi_ok);
|
||||
INTEROP_FATAL_IF(IsUndefined(env, modObj));
|
||||
}
|
||||
INTEROP_FATAL_IF(IsUndefined(env, modObj));
|
||||
|
||||
return JSValue::CreateRefValue(coro, ctx, modObj, napi_object);
|
||||
}
|
||||
|
@ -412,7 +412,7 @@ JSCONVERT_UNWRAP(ArrayBuffer)
|
||||
EtsHandle<EtsArrayBuffer> buf(currentCoro,
|
||||
reinterpret_cast<EtsArrayBuffer *>(EtsObject::Create(currentCoro, arraybufKlass)));
|
||||
buf->SetByteLength(static_cast<EtsInt>(byteLength));
|
||||
buf->SetData(currentCoro, EtsArray::CreateForPrimitive<EtsByteArray>(EtsClassRoot::BYTE_ARRAY, byteLength));
|
||||
buf->SetData(currentCoro, EtsByteArray::Create(byteLength));
|
||||
std::copy_n(reinterpret_cast<uint8_t *>(data), byteLength, buf->GetData()->GetData<EtsByte>());
|
||||
return buf.GetPtr();
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ extern "C" ObjectHeader *EtsArrayBufferFrom(EtsObject *obj)
|
||||
}
|
||||
auto byteLength = static_cast<EtsInt>(array->GetLength() * array->GetElementSize());
|
||||
buf->SetByteLength(byteLength);
|
||||
auto *data = EtsArray::CreateForPrimitive<EtsByteArray>(EtsClassRoot::BYTE_ARRAY, byteLength);
|
||||
auto *data = EtsByteArray::Create(byteLength);
|
||||
buf->SetData(coro, data);
|
||||
if (UNLIKELY(buf->GetData() == nullptr)) {
|
||||
return nullptr;
|
||||
|
@ -85,19 +85,6 @@ public:
|
||||
return reinterpret_cast<coretypes::Array *>(this);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static T *Create(EtsClass *arrayClass, uint32_t length, SpaceType spaceType = SpaceType::SPACE_TYPE_OBJECT)
|
||||
{
|
||||
return reinterpret_cast<T *>(coretypes::Array::Create(arrayClass->GetRuntimeClass(), length, spaceType));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static T *CreateForPrimitive(EtsClassRoot root, uint32_t length, SpaceType spaceType = SpaceType::SPACE_TYPE_OBJECT)
|
||||
{
|
||||
EtsClass *arrayClass = PandaEtsVM::GetCurrent()->GetClassLinker()->GetClassRoot(root);
|
||||
return Create<T>(arrayClass, length, spaceType);
|
||||
}
|
||||
|
||||
NO_COPY_SEMANTIC(EtsArray);
|
||||
NO_MOVE_SEMANTIC(EtsArray);
|
||||
|
||||
@ -105,6 +92,12 @@ protected:
|
||||
// Use type alias to allow using into derived classes
|
||||
using ObjectHeader = ::ark::ObjectHeader;
|
||||
|
||||
template <class T>
|
||||
static T *Create(EtsClass *arrayClass, uint32_t length, SpaceType spaceType = SpaceType::SPACE_TYPE_OBJECT)
|
||||
{
|
||||
return reinterpret_cast<T *>(coretypes::Array::Create(arrayClass->GetRuntimeClass(), length, spaceType));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void SetImpl(uint32_t idx, T elem)
|
||||
{
|
||||
@ -121,6 +114,8 @@ protected:
|
||||
template <typename Component>
|
||||
class EtsTypedObjectArray : public EtsArray {
|
||||
public:
|
||||
using ValueType = Component *;
|
||||
|
||||
static EtsTypedObjectArray *Create(EtsClass *objectClass, uint32_t length,
|
||||
ark::SpaceType spaceType = ark::SpaceType::SPACE_TYPE_OBJECT)
|
||||
{
|
||||
@ -219,8 +214,7 @@ public:
|
||||
static EtsPrimitiveArray *Create(uint32_t length, SpaceType spaceType = SpaceType::SPACE_TYPE_OBJECT)
|
||||
{
|
||||
ASSERT_HAVE_ACCESS_TO_MANAGED_OBJECTS();
|
||||
// NOLINTNEXTLINE(readability-magic-numbers)
|
||||
return EtsArray::CreateForPrimitive<EtsPrimitiveArray>(ETS_CLASS_ROOT, length, spaceType);
|
||||
return EtsArray::Create<EtsPrimitiveArray>(GetComponentClass(), length, spaceType);
|
||||
}
|
||||
void Set(uint32_t index, ClassType element)
|
||||
{
|
||||
@ -230,6 +224,10 @@ public:
|
||||
{
|
||||
return GetImpl<ClassType>(index);
|
||||
}
|
||||
static EtsClass *GetComponentClass()
|
||||
{
|
||||
return PandaEtsVM::GetCurrent()->GetClassLinker()->GetClassRoot(ETS_CLASS_ROOT);
|
||||
}
|
||||
|
||||
EtsPrimitiveArray() = delete;
|
||||
~EtsPrimitiveArray() = delete;
|
||||
|
@ -52,7 +52,7 @@ function(panda_ets_interop_js_gtest TARGET)
|
||||
set(INTEROP_TESTS_DIR "${PANDA_BINARY_ROOT}/tests/ets_interop_js")
|
||||
panda_ets_interop_js_plugin(${TARGET}
|
||||
SOURCES ${ARG_CPP_SOURCES}
|
||||
LIBRARIES ets_interop_js_gtest ${ARG_LIBRARIES}
|
||||
LIBRARIES ets_interop_js_gtest ets_interop_js_napi ${ARG_LIBRARIES}
|
||||
LIBRARY_OUTPUT_DIRECTORY "${INTEROP_TESTS_DIR}/lib/module"
|
||||
)
|
||||
|
||||
|
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
const { etsVm, getTestModule } = require('scenarios.test.js');
|
||||
|
||||
const etsMod = getTestModule('scenarios_test');
|
||||
|
||||
let ets_sum_rest_params = etsMod.getFunction("ets_sum_rest_params");
|
||||
let ets_multiply_1arg_by_sum_rest_params = etsMod.getFunction("ets_multiply_1arg_by_sum_rest_params");
|
||||
let ets_multiply_sum2args_by_sum_rest_params = etsMod.getFunction("ets_multiply_sum2args_by_sum_rest_params");
|
||||
let ets_concat_strings_rest_params = etsMod.getFunction("ets_concat_strings_rest_params");
|
||||
let ets_method_rest_params = etsMod.getFunction("ets_call_foo_rest_params");
|
||||
let F = etsMod.getClass("RestParamsTest");
|
||||
{
|
||||
ASSERT_EQ(ets_sum_rest_params(1, 2, 3), (1 + 2 + 3));
|
||||
ASSERT_EQ(ets_multiply_1arg_by_sum_rest_params(1, 2, 3, 4), (1)*(2+3+4));
|
||||
ASSERT_EQ(ets_multiply_sum2args_by_sum_rest_params(1, 2, 3, 4, 5), (1+2)*(3+4+5));
|
||||
ASSERT_EQ(ets_concat_strings_rest_params(), "");
|
||||
ASSERT_EQ(ets_concat_strings_rest_params('a', 'b', 'c', 'd'), "abcd");
|
||||
ASSERT_EQ(ets_method_rest_params(new F(), new F(), new F()), 9);
|
||||
let foo = new F();
|
||||
ASSERT_EQ(foo.sum_ints(1, 2, 3), (1 + 2 + 3));
|
||||
}
|
@ -173,4 +173,9 @@ TEST_F(EtsInteropScenariosEtsToJs, test_function_return_type_primitive)
|
||||
ASSERT_EQ(true, RunJsTestSuite("js_suites/test_function_return_type_primitive.js"));
|
||||
}
|
||||
|
||||
TEST_F(EtsInteropScenariosEtsToJs, test_rest_params)
|
||||
{
|
||||
ASSERT_EQ(true, RunJsTestSuite("js_suites/test_rest_params_call.js"));
|
||||
}
|
||||
|
||||
} // namespace ark::ets::interop::js::testing
|
||||
|
@ -174,6 +174,61 @@ function function_return_type_primitive(): number {
|
||||
return 1;
|
||||
}
|
||||
|
||||
function ets_sum_rest_params(...args: number[]): number {
|
||||
let sum = 0;
|
||||
for (let n of args) {
|
||||
sum += n;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
function ets_multiply_1arg_by_sum_rest_params(arg0: number, ...args: number[]): number {
|
||||
let sum = 0;
|
||||
for (let n of args) {
|
||||
sum += n;
|
||||
}
|
||||
return arg0 * sum;
|
||||
}
|
||||
|
||||
function ets_multiply_sum2args_by_sum_rest_params(arg0: number, arg1: number, ...args: number[]): number {
|
||||
let sum = 0;
|
||||
for (let n of args) {
|
||||
sum += n;
|
||||
}
|
||||
return sum * (arg0 + arg1);
|
||||
}
|
||||
|
||||
class RestParamsTest {
|
||||
x = 2;
|
||||
foo() {
|
||||
return 1;
|
||||
}
|
||||
sum_ints(...args: Int[]) {
|
||||
let sum = 0;
|
||||
for (let n of args) {
|
||||
sum += n;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
};
|
||||
|
||||
function ets_call_foo_rest_params(...args: RestParamsTest[]): number {
|
||||
let sum = 0;
|
||||
for (let s of args) {
|
||||
sum += s.foo();
|
||||
sum += s.x;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
function ets_concat_strings_rest_params(...args: String[]): String {
|
||||
let str = "";
|
||||
for (let s of args) {
|
||||
str += s;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
function GCJSRuntimeCleanup(): void {
|
||||
try {
|
||||
// trigger FinalizationRegistry cleanup
|
||||
|
@ -176,6 +176,30 @@ function single_required(z, x=123, y=123) {
|
||||
return x + y + z;
|
||||
}
|
||||
|
||||
function js_sum_rest_params(...args) {
|
||||
let sum = 0;
|
||||
args.forEach(n => sum += n);
|
||||
return sum;
|
||||
}
|
||||
|
||||
function js_multiply_1arg_by_sum_rest_params(arg0, ...args) {
|
||||
let sum = 0;
|
||||
args.forEach(n => sum += n);
|
||||
return sum * (arg0);
|
||||
}
|
||||
|
||||
function js_multiply_sum2args_by_sum_rest_params(arg0, arg1, ...args) {
|
||||
let sum = 0;
|
||||
args.forEach(n => sum += n);
|
||||
return sum * (arg0 + arg1);
|
||||
}
|
||||
|
||||
function js_concat_strings_rest_params(...args) {
|
||||
let str = "";
|
||||
args.forEach(s => str += s);
|
||||
return str;
|
||||
}
|
||||
|
||||
exports.standaloneFunctionJs = standaloneFunctionJs;
|
||||
exports.ClassWithMethodJs = ClassWithMethodJs;
|
||||
exports.newInterfaceWithMethod = newInterfaceWithMethod;
|
||||
@ -202,3 +226,7 @@ exports.functionArgTypePrimitive = functionArgTypePrimitive;
|
||||
exports.functionReturnTypePrimitive = functionReturnTypePrimitive;
|
||||
exports.optional_call = optional_call;
|
||||
exports.single_required = single_required;
|
||||
exports.js_sum_rest_params = js_sum_rest_params;
|
||||
exports.js_multiply_1arg_by_sum_rest_params = js_multiply_1arg_by_sum_rest_params;
|
||||
exports.js_multiply_sum2args_by_sum_rest_params = js_multiply_sum2args_by_sum_rest_params;
|
||||
exports.js_concat_strings_rest_params = js_concat_strings_rest_params;
|
||||
|
@ -168,4 +168,10 @@ TEST_F(EtsInteropScenariosJsToEts, Test_optional_call)
|
||||
ASSERT_EQ(ret, true);
|
||||
}
|
||||
|
||||
TEST_F(EtsInteropScenariosJsToEts, Test_rest_params)
|
||||
{
|
||||
auto ret = CallEtsMethod<bool>("Test_rest_params");
|
||||
ASSERT_EQ(ret, true);
|
||||
}
|
||||
|
||||
} // namespace ark::ets::interop::js::testing
|
||||
|
@ -18,7 +18,9 @@ import { standaloneFunctionJs, ClassWithMethodJs, newInterfaceWithMethod, ClassW
|
||||
functionArgTypeCallable, functionDefaultParameterFunction, functionDefaultIntParameterFunction,
|
||||
functionDefaultStringParameterFunction, functionDefaultFloatParameterFunction, genericTypeParameter,
|
||||
genericTypeReturnValue, functionArgTypeOptionalPrimitive, functionArgTypePrimitive,
|
||||
functionReturnTypePrimitive } from "pure_js"
|
||||
functionReturnTypePrimitive, js_sum_rest_params,
|
||||
js_multiply_1arg_by_sum_rest_params, js_multiply_sum2args_by_sum_rest_params,
|
||||
js_concat_strings_rest_params } from "pure_js"
|
||||
|
||||
import {
|
||||
optional_call,
|
||||
@ -161,3 +163,13 @@ function Test_function_return_type_primitive(): boolean {
|
||||
let res: boolean = functionReturnTypePrimitive() as boolean;
|
||||
return typeof res === "boolean";
|
||||
}
|
||||
|
||||
function Test_rest_params() {
|
||||
// NOTE: casts due to bug in frontend
|
||||
assert js_sum_rest_params(1, 2, 3) as Int == (1+2+3)
|
||||
assert js_multiply_1arg_by_sum_rest_params(1, 2, 3, 4) as Int == (1) * (2+3+4)
|
||||
assert js_multiply_sum2args_by_sum_rest_params(1, 2, 3, 4, 5) as Int == (1+2)*(3+4+5);
|
||||
assert js_concat_strings_rest_params() as String == "";
|
||||
assert js_concat_strings_rest_params("a", "b", "c") as String == "abc";
|
||||
return true;
|
||||
}
|
||||
|
@ -557,6 +557,13 @@ public:
|
||||
return (accessFlags_.load(std::memory_order_acquire) & ACC_SYNCHRONIZED) != 0;
|
||||
}
|
||||
|
||||
bool HasVarArgs() const
|
||||
{
|
||||
// Atomic with acquire order reason: data race with access_flags_ with dependecies on reads after the load which
|
||||
// should become visible
|
||||
return (accessFlags_.load(std::memory_order_acquire) & ACC_VARARGS) != 0;
|
||||
}
|
||||
|
||||
bool HasSingleImplementation() const
|
||||
{
|
||||
// Atomic with acquire order reason: data race with access_flags_ with dependecies on reads after the load which
|
||||
|
Loading…
Reference in New Issue
Block a user