* 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
*
* 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.
*/
#include "ecmascript/builtins/builtins_reflect.h"
#include "ecmascript/interpreter/interpreter.h"
#include "ecmascript/js_object-inl.h"
namespace panda::ecmascript::builtins {
JSTaggedValue BuiltinsReflect::ReflectApply(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Reflect, Apply);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> target = GetCallArg(argv, 0);
JSHandle<JSTaggedValue> thisArgument = GetCallArg(argv, 1);
JSHandle<JSTaggedValue> argumentsList = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD);
return ReflectApplyInternal(thread, target, thisArgument, argumentsList);
}
JSTaggedValue BuiltinsReflect::ReflectApplyInternal(JSThread *thread, JSHandle<JSTaggedValue> target,
JSHandle<JSTaggedValue> thisArgument,
JSHandle<JSTaggedValue> argumentsList)
{
[[maybe_unused]] EcmaHandleScope handleScope(thread);
if (!target->IsCallable()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.apply target is not callable", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> argOrAbrupt = JSObject::CreateListFromArrayLike(thread, argumentsList);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<TaggedArray> args = JSHandle<TaggedArray>::Cast(argOrAbrupt);
const uint32_t argsLength = args->GetLength();
JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
EcmaRuntimeCallInfo *info =
EcmaInterpreter::NewRuntimeCallInfo(thread, target, thisArgument, undefined, argsLength);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(argsLength, args);
return JSFunction::Call(info);
}
JSTaggedValue BuiltinsReflect::ReflectConstruct(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Reflect, Constructor);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> target = GetCallArg(argv, 0);
if (!target->IsConstructor()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.construct target is not constructor", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> newTarget =
argv->GetArgsNumber() > 2 ? GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD) : target;
if (!newTarget->IsConstructor()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.construct newTarget is present, but not constructor",
JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> argumentsList = GetCallArg(argv, 1);
JSHandle<JSTaggedValue> argOrAbrupt = JSObject::CreateListFromArrayLike(thread, argumentsList);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<TaggedArray> args = JSHandle<TaggedArray>::Cast(argOrAbrupt);
return ReflectConstructInternal(thread, target, args, newTarget);
}
JSTaggedValue BuiltinsReflect::ReflectConstructInternal(JSThread *thread, JSHandle<JSTaggedValue> target,
JSHandle<TaggedArray> args, JSHandle<JSTaggedValue> newTarget)
{
[[maybe_unused]] EcmaHandleScope handleScope(thread);
const uint32_t argsLength = args->GetLength();
JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, target, undefined, newTarget, argsLength);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(argsLength, args);
return JSFunction::Construct(info);
}
JSTaggedValue BuiltinsReflect::ReflectDefineProperty(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Reflect, DefineProperty);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> target = GetCallArg(argv, 0);
if (!target->IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.defineProperty target is not object", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> key = JSTaggedValue::ToPropertyKey(thread, GetCallArg(argv, 1));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> attributes = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD);
PropertyDescriptor desc(thread);
JSObject::ToPropertyDescriptor(thread, attributes, desc);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return GetTaggedBoolean(JSTaggedValue::DefineOwnProperty(thread, target, key, desc));
}
JSTaggedValue BuiltinsReflect::ReflectDeleteProperty(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Reflect, DeleteProperty);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> target = GetCallArg(argv, 0);
if (!target->IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.deleteProperty target is not object", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> key = JSTaggedValue::ToPropertyKey(thread, GetCallArg(argv, 1));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return GetTaggedBoolean(JSTaggedValue::DeleteProperty(thread, target, key));
}
JSTaggedValue BuiltinsReflect::ReflectGet(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Reflect, Get);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> val = GetCallArg(argv, 0);
if (!val->IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.get target is not object", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> key = JSTaggedValue::ToPropertyKey(thread, GetCallArg(argv, 1));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (argv->GetArgsNumber() == 2) {
return JSTaggedValue::GetProperty(thread, val, key).GetValue().GetTaggedValue();
}
JSHandle<JSTaggedValue> receiver = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD);
return JSTaggedValue::GetProperty(thread, val, key, receiver).GetValue().GetTaggedValue();
}
JSTaggedValue BuiltinsReflect::ReflectGetOwnPropertyDescriptor(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Reflect, GetOwnPropertyDescriptor);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> target = GetCallArg(argv, 0);
if (!target->IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.getOwnPropertyDescriptor target is not object",
JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> key = JSTaggedValue::ToPropertyKey(thread, GetCallArg(argv, 1));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
PropertyDescriptor desc(thread);
if (!JSTaggedValue::GetOwnProperty(thread, target, key, desc)) {
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
JSHandle<JSTaggedValue> res = JSObject::FromPropertyDescriptor(thread, desc);
return res.GetTaggedValue();
}
JSTaggedValue BuiltinsReflect::ReflectGetPrototypeOf(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Reflect, GetPrototypeOf);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> val = GetCallArg(argv, 0);
if (!val->IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.getPrototypeOf target is not object", JSTaggedValue::Exception());
}
return JSTaggedValue::GetPrototype(thread, val);
}
JSTaggedValue BuiltinsReflect::ReflectHas(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Reflect, Has);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> target = GetCallArg(argv, 0);
JSHandle<JSTaggedValue> key = GetCallArg(argv, 1);
return ReflectHasInternal(thread, target, key);
}
JSTaggedValue BuiltinsReflect::ReflectHasInternal(JSThread *thread, JSHandle<JSTaggedValue> target,
JSHandle<JSTaggedValue> key)
{
[[maybe_unused]] EcmaHandleScope handleScope(thread);
if (!target->IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.has target is not object", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> propertyKey = JSTaggedValue::ToPropertyKey(thread, key);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return GetTaggedBoolean(JSTaggedValue::HasProperty(thread, target, propertyKey));
}
JSTaggedValue BuiltinsReflect::ReflectIsExtensible(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> target = GetCallArg(argv, 0);
if (!target->IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.isExtensible target is not object", JSTaggedValue::Exception());
}
return GetTaggedBoolean(target->IsExtensible(thread));
}
JSTaggedValue BuiltinsReflect::ReflectOwnKeys(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Reflect, OwnKeys);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> target = GetCallArg(argv, 0);
if (!target->IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.ownKeys target is not object", JSTaggedValue::Exception());
}
JSHandle<TaggedArray> keys = JSTaggedValue::GetOwnPropertyKeys(thread, target);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSArray> result = JSArray::CreateArrayFromList(thread, keys);
return result.GetTaggedValue();
}
JSTaggedValue BuiltinsReflect::ReflectPreventExtensions(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Reflect, PreventExtensions);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> target = GetCallArg(argv, 0);
if (!target->IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.preventExtensions target is not object",
JSTaggedValue::Exception());
}
return GetTaggedBoolean(JSTaggedValue::PreventExtensions(thread, target));
}
JSTaggedValue BuiltinsReflect::ReflectSet(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Reflect, Set);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> targetVal = GetCallArg(argv, 0);
if (!targetVal->IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.set target is not object", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> key = JSTaggedValue::ToPropertyKey(thread, GetCallArg(argv, 1));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> value = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD);
if (argv->GetArgsNumber() <= BuiltinsBase::ArgsPosition::FOURTH) {
return GetTaggedBoolean(JSTaggedValue::SetProperty(thread, targetVal, key, value));
}
JSHandle<JSTaggedValue> receiver = GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH);
return GetTaggedBoolean(JSTaggedValue::SetProperty(thread, targetVal, key, value, receiver));
}
JSTaggedValue BuiltinsReflect::ReflectSetPrototypeOf(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Reflect, SetPrototypeOf);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> target = GetCallArg(argv, 0);
if (!target->IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.setPrototypeOf target is not object", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> proto = GetCallArg(argv, 1);
if (!proto->IsECMAObject() && !proto->IsNull()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "SetPrototypeOf: proto is neither Object nor Null",
JSTaggedValue::Exception());
}
return GetTaggedBoolean(JSTaggedValue::SetPrototype(thread, target, proto));
}
}