arkcompiler_ets_runtime/ecmascript/stubs/runtime_stubs-inl.h
openharmony_ci aad409e815
!7689 Disable worker pgo profiler and aot optimization
Merge pull request !7689 from yingguofeng/master
2024-06-07 09:52:39 +00:00

3333 lines
157 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2021 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.
*/
#ifndef ECMASCRIPT_STUBS_RUNTIME_STUBS_INL_H
#define ECMASCRIPT_STUBS_RUNTIME_STUBS_INL_H
#include "ecmascript/ecma_macros.h"
#include "ecmascript/js_tagged_value.h"
#include "ecmascript/stubs/runtime_stubs.h"
#include "ecmascript/base/array_helper.h"
#include "ecmascript/builtins/builtins_regexp.h"
#include "ecmascript/compiler/aot_file/aot_file_manager.h"
#include "ecmascript/ecma_string_table.h"
#include "ecmascript/global_dictionary-inl.h"
#include "ecmascript/global_env.h"
#include "ecmascript/ic/profile_type_info.h"
#include "ecmascript/interpreter/frame_handler.h"
#include "ecmascript/jobs/micro_job_queue.h"
#include "ecmascript/js_arguments.h"
#include "ecmascript/js_async_function.h"
#include "ecmascript/js_async_generator_object.h"
#include "ecmascript/js_bigint.h"
#include "ecmascript/js_for_in_iterator.h"
#include "ecmascript/js_generator_object.h"
#include "ecmascript/js_iterator.h"
#include "ecmascript/js_promise.h"
#include "ecmascript/jspandafile/class_info_extractor.h"
#include "ecmascript/jspandafile/literal_data_extractor.h"
#include "ecmascript/jspandafile/scope_info_extractor.h"
#include "ecmascript/module/js_module_manager.h"
#include "ecmascript/module/js_module_source_text.h"
#include "ecmascript/patch/quick_fix_helper.h"
#include "ecmascript/platform/file.h"
#include "ecmascript/runtime.h"
#include "ecmascript/stackmap/llvm/llvm_stackmap_parser.h"
#include "ecmascript/sendable_env.h"
#include "ecmascript/template_string.h"
namespace panda::ecmascript {
using ArrayHelper = base::ArrayHelper;
JSTaggedValue RuntimeStubs::RuntimeInc(JSThread *thread, const JSHandle<JSTaggedValue> &value)
{
JSHandle<JSTaggedValue> inputVal = JSTaggedValue::ToNumeric(thread, value);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (inputVal->IsBigInt()) {
JSHandle<BigInt> bigValue(inputVal);
return BigInt::BigintAddOne(thread, bigValue).GetTaggedValue();
}
JSTaggedNumber number(inputVal.GetTaggedValue());
return JSTaggedValue(++number);
}
JSTaggedValue RuntimeStubs::RuntimeDec(JSThread *thread, const JSHandle<JSTaggedValue> &value)
{
JSHandle<JSTaggedValue> inputVal = JSTaggedValue::ToNumeric(thread, value);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (inputVal->IsBigInt()) {
JSHandle<BigInt> bigValue(inputVal);
return BigInt::BigintSubOne(thread, bigValue).GetTaggedValue();
}
JSTaggedNumber number(inputVal.GetTaggedValue());
return JSTaggedValue(--number);
}
JSTaggedValue RuntimeStubs::RuntimeExp(JSThread *thread, JSTaggedValue base, JSTaggedValue exponent)
{
JSHandle<JSTaggedValue> baseTag(thread, base);
JSHandle<JSTaggedValue> exponentTag(thread, exponent);
JSHandle<JSTaggedValue> valBase = JSTaggedValue::ToNumeric(thread, baseTag);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> valExponent = JSTaggedValue::ToNumeric(thread, exponentTag);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (valBase->IsBigInt() || valExponent->IsBigInt()) {
if (valBase->IsBigInt() && valExponent->IsBigInt()) {
JSHandle<BigInt> bigBaseVale(valBase);
JSHandle<BigInt> bigExponentValue(valExponent);
return BigInt::Exponentiate(thread, bigBaseVale, bigExponentValue).GetTaggedValue();
}
THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot mix BigInt and other types, use explicit conversions",
JSTaggedValue::Exception());
}
double doubleBase = valBase->GetNumber();
double doubleExponent = valExponent->GetNumber();
if (std::abs(doubleBase) == 1 && std::isinf(doubleExponent)) {
return JSTaggedValue(base::NAN_VALUE);
}
if (((doubleBase == 0) &&
((base::bit_cast<uint64_t>(doubleBase)) & base::DOUBLE_SIGN_MASK) == base::DOUBLE_SIGN_MASK) &&
std::isfinite(doubleExponent) && base::NumberHelper::TruncateDouble(doubleExponent) == doubleExponent &&
base::NumberHelper::TruncateDouble(doubleExponent / 2) + base::HALF == (doubleExponent / 2)) { // 2: half
if (doubleExponent > 0) {
return JSTaggedValue(-0.0);
}
if (doubleExponent < 0) {
return JSTaggedValue(-base::POSITIVE_INFINITY);
}
}
return JSTaggedValue(std::pow(doubleBase, doubleExponent));
}
JSTaggedValue RuntimeStubs::RuntimeIsIn(JSThread *thread, const JSHandle<JSTaggedValue> &prop,
const JSHandle<JSTaggedValue> &obj)
{
if (!obj->IsECMAObject()) {
return RuntimeThrowTypeError(thread, "Cannot use 'in' operator in Non-Object");
}
JSHandle<JSTaggedValue> propKey = JSTaggedValue::ToPropertyKey(thread, prop);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
bool ret = JSTaggedValue::HasProperty(thread, obj, propKey);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return JSTaggedValue(ret);
}
JSTaggedValue RuntimeStubs::RuntimeInstanceof(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
const JSHandle<JSTaggedValue> &target)
{
bool ret = JSObject::InstanceOf(thread, obj, target);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return JSTaggedValue(ret);
}
JSTaggedValue RuntimeStubs::RuntimeInstanceofByHandler(JSThread *thread, JSHandle<JSTaggedValue> target,
JSHandle<JSTaggedValue> object,
JSHandle<JSTaggedValue> instOfHandler)
{
// 3. ReturnIfAbrupt(instOfHandler).
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 4. If instOfHandler is not undefined, then
if (!instOfHandler->IsUndefined()) {
// a. Return ! ToBoolean(? Call(instOfHandler, target, «object»)).
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
JSTaggedValue function = env->GetTaggedHasInstanceFunction();
JSTaggedValue instOf = instOfHandler.GetTaggedValue();
// slowpath
if (function != instOf) {
JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, instOfHandler, target,
undefined, 1);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(object.GetTaggedValue());
JSTaggedValue tagged = JSFunction::Call(info);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return tagged;
}
}
// 5. If IsCallable(target) is false, throw a TypeError exception.
if (!target->IsCallable()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "InstanceOf error when target is not Callable", JSTaggedValue::Exception());
}
// fastpath
// 6. Return ? OrdinaryHasInstance(target, object).
bool res = JSFunction::OrdinaryHasInstance(thread, target, object);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return JSTaggedValue(res);
}
JSTaggedValue RuntimeStubs::RuntimeCreateGeneratorObj(JSThread *thread, const JSHandle<JSTaggedValue> &genFunc)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<JSGeneratorObject> obj = factory->NewJSGeneratorObject(genFunc);
JSHandle<GeneratorContext> context = factory->NewGeneratorContext();
context->SetGeneratorObject(thread, obj.GetTaggedValue());
// change state to SUSPENDED_START
obj->SetGeneratorState(JSGeneratorState::SUSPENDED_START);
obj->SetGeneratorContext(thread, context);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return obj.GetTaggedValue();
}
JSTaggedValue RuntimeStubs::RuntimeCreateAsyncGeneratorObj(JSThread *thread, const JSHandle<JSTaggedValue> &genFunc)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<JSAsyncGeneratorObject> obj = factory->NewJSAsyncGeneratorObject(genFunc);
JSHandle<GeneratorContext> context = factory->NewGeneratorContext();
context->SetGeneratorObject(thread, obj.GetTaggedValue());
// change state to SUSPENDED_START
obj->SetAsyncGeneratorState(JSAsyncGeneratorState::SUSPENDED_START);
obj->SetGeneratorContext(thread, context);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return obj.GetTaggedValue();
}
JSTaggedValue RuntimeStubs::RuntimeGetTemplateObject(JSThread *thread, const JSHandle<JSTaggedValue> &literal)
{
JSHandle<JSTaggedValue> templateObj = TemplateString::GetTemplateObject(thread, literal);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return templateObj.GetTaggedValue();
}
JSTaggedValue RuntimeStubs::RuntimeGetNextPropName(JSThread *thread, const JSHandle<JSTaggedValue> &iter)
{
ASSERT(iter->IsForinIterator());
JSTaggedValue res = JSForInIterator::NextInternal(thread, JSHandle<JSForInIterator>::Cast(iter));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return res;
}
JSTaggedValue RuntimeStubs::RuntimeIterNext(JSThread *thread, const JSHandle<JSTaggedValue> &iter)
{
JSHandle<JSTaggedValue> resultObj = JSIterator::IteratorNext(thread, iter);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return resultObj.GetTaggedValue();
}
JSTaggedValue RuntimeStubs::RuntimeCloseIterator(JSThread *thread, const JSHandle<JSTaggedValue> &iter)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
JSHandle<JSTaggedValue> record;
if (thread->HasPendingException()) {
record = JSHandle<JSTaggedValue>(factory->NewCompletionRecord(
CompletionRecordType::THROW, JSHandle<JSTaggedValue>(thread, thread->GetException())));
} else {
JSHandle<JSTaggedValue> undefinedVal = globalConst->GetHandledUndefined();
record = JSHandle<JSTaggedValue>(factory->NewCompletionRecord(CompletionRecordType::NORMAL, undefinedVal));
}
JSHandle<JSTaggedValue> result = JSIterator::IteratorClose(thread, iter, record);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (result->IsCompletionRecord()) {
return CompletionRecord::Cast(result->GetTaggedObject())->GetValue();
}
return result.GetTaggedValue();
}
JSTaggedValue RuntimeStubs::RuntimeSuperCallSpread(JSThread *thread, const JSHandle<JSTaggedValue> &func,
const JSHandle<JSTaggedValue> &newTarget,
const JSHandle<JSTaggedValue> &array)
{
JSHandle<JSTaggedValue> superFunc(thread, JSTaggedValue::GetPrototype(thread, func));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (!superFunc->IsJSFunction()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Super constructor is not JSFunction", JSTaggedValue::Exception());
}
JSHandle<TaggedArray> argv(thread, RuntimeGetCallSpreadArgs(thread, array));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
const uint32_t argsLength = argv->GetLength();
JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
EcmaRuntimeCallInfo *info =
EcmaInterpreter::NewRuntimeCallInfo(thread, superFunc, undefined, newTarget, argsLength);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(argsLength, argv);
JSTaggedValue result = JSFunction::Construct(info);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return result;
}
JSTaggedValue RuntimeStubs::RuntimeDelObjProp(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
const JSHandle<JSTaggedValue> &prop)
{
JSHandle<JSTaggedValue> jsObj(JSTaggedValue::ToObject(thread, obj));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> propKey = JSTaggedValue::ToPropertyKey(thread, prop);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
bool ret = JSTaggedValue::DeletePropertyOrThrow(thread, jsObj, propKey);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return JSTaggedValue(ret);
}
JSTaggedValue RuntimeStubs::RuntimeNewObjApply(JSThread *thread, const JSHandle<JSTaggedValue> &func,
const JSHandle<JSTaggedValue> &array)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
if (!array->IsJSArray()) {
return RuntimeThrowTypeError(thread, "Cannot Newobjspread");
}
uint32_t length = JSHandle<JSArray>::Cast(array)->GetArrayLength();
JSHandle<TaggedArray> argsArray = factory->NewTaggedArray(length);
for (uint32_t i = 0; i < length; ++i) {
auto prop = JSTaggedValue::GetProperty(thread, array, i).GetValue();
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
argsArray->Set(thread, i, prop);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, undefined, func, length);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(length, argsArray);
return NewObject(info);
}
JSTaggedValue RuntimeStubs::RuntimeCreateIterResultObj(JSThread *thread, const JSHandle<JSTaggedValue> &value,
JSTaggedValue flag)
{
ASSERT(flag.IsBoolean());
bool done = flag.IsTrue();
JSHandle<JSObject> iter = JSIterator::CreateIterResultObject(thread, value, done);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return iter.GetTaggedValue();
}
JSTaggedValue RuntimeStubs::RuntimeAsyncFunctionAwaitUncaught(JSThread *thread,
const JSHandle<JSTaggedValue> &asyncFuncObj,
const JSHandle<JSTaggedValue> &value)
{
JSAsyncFunction::AsyncFunctionAwait(thread, asyncFuncObj, value);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (asyncFuncObj->IsAsyncGeneratorObject()) {
JSHandle<JSObject> obj = JSTaggedValue::ToObject(thread, asyncFuncObj);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSAsyncGeneratorObject> generator = JSHandle<JSAsyncGeneratorObject>::Cast(obj);
JSHandle<TaggedQueue> queue(thread, generator->GetAsyncGeneratorQueue());
if (queue->Empty()) {
return JSTaggedValue::Undefined();
}
JSHandle<AsyncGeneratorRequest> next(thread, queue->Front());
JSHandle<PromiseCapability> completion(thread, next->GetCapability());
JSHandle<JSPromise> promise(thread, completion->GetPromise());
return promise.GetTaggedValue();
}
JSHandle<JSAsyncFuncObject> asyncFuncObjHandle(asyncFuncObj);
JSHandle<JSPromise> promise(thread, asyncFuncObjHandle->GetPromise());
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return promise.GetTaggedValue();
}
JSTaggedValue RuntimeStubs::RuntimeAsyncFunctionResolveOrReject(JSThread *thread,
const JSHandle<JSTaggedValue> &asyncFuncObj, const JSHandle<JSTaggedValue> &value, bool is_resolve)
{
JSHandle<JSAsyncFuncObject> asyncFuncObjHandle(asyncFuncObj);
JSHandle<JSPromise> promise(thread, asyncFuncObjHandle->GetPromise());
// ActivePromise
JSHandle<ResolvingFunctionsRecord> reactions = JSPromise::CreateResolvingFunctions(thread, promise);
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
JSHandle<JSTaggedValue> thisArg = globalConst->GetHandledUndefined();
JSHandle<JSTaggedValue> activeFunc;
if (is_resolve) {
activeFunc = JSHandle<JSTaggedValue>(thread, reactions->GetResolveFunction());
} else {
activeFunc = JSHandle<JSTaggedValue>(thread, reactions->GetRejectFunction());
}
JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, activeFunc, thisArg, undefined, 1);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(value.GetTaggedValue());
[[maybe_unused]] JSTaggedValue res = JSFunction::Call(info);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return promise.GetTaggedValue();
}
JSTaggedValue RuntimeStubs::RuntimeAsyncGeneratorResolve(JSThread *thread, JSHandle<JSTaggedValue> asyncFuncObj,
JSHandle<JSTaggedValue> value, JSTaggedValue flag)
{
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSAsyncGeneratorObject> asyncGeneratorObjHandle(asyncFuncObj);
JSHandle<JSTaggedValue> valueHandle(value);
JSHandle<GeneratorContext> genContextHandle(thread, asyncGeneratorObjHandle->GetGeneratorContext());
// save stack, should copy cur_frame, function execute over will free cur_frame
SaveFrameToContext(thread, genContextHandle);
ASSERT(flag.IsBoolean());
bool done = flag.IsTrue();
return JSAsyncGeneratorObject::AsyncGeneratorResolve(thread, asyncGeneratorObjHandle, valueHandle, done);
}
JSTaggedValue RuntimeStubs::RuntimeAsyncGeneratorReject(JSThread *thread, JSHandle<JSTaggedValue> asyncFuncObj,
JSHandle<JSTaggedValue> value)
{
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSAsyncGeneratorObject> asyncGeneratorObjHandle(asyncFuncObj);
JSHandle<JSTaggedValue> valueHandle(value);
return JSAsyncGeneratorObject::AsyncGeneratorReject(thread, asyncGeneratorObjHandle, valueHandle);
}
JSTaggedValue RuntimeStubs::RuntimeCopyDataProperties(JSThread *thread, const JSHandle<JSTaggedValue> &dst,
const JSHandle<JSTaggedValue> &src)
{
if (!src->IsNull() && !src->IsUndefined()) {
// 2. Let from be ! ToObject(source).
JSHandle<JSTaggedValue> from(JSTaggedValue::ToObject(thread, src));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<TaggedArray> keys = JSTaggedValue::GetOwnPropertyKeys(thread, from);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
uint32_t keysLen = keys->GetLength();
for (uint32_t i = 0; i < keysLen; i++) {
PropertyDescriptor desc(thread);
key.Update(keys->Get(i));
bool success = JSTaggedValue::GetOwnProperty(thread, from, key, desc);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (success && desc.IsEnumerable()) {
desc.SetWritable(true);
desc.SetConfigurable(true);
JSTaggedValue::DefineOwnProperty(thread, dst, key, desc);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
}
}
return dst.GetTaggedValue();
}
JSTaggedValue RuntimeStubs::RuntimeStArraySpread(JSThread *thread, const JSHandle<JSTaggedValue> &dst,
JSTaggedValue index, const JSHandle<JSTaggedValue> &src)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<JSTaggedValue> undefinedHandle(thread, JSTaggedValue::Undefined());
ASSERT(dst->IsJSArray());
if (dst->IsJSArray()) {
if (src->IsNull() || src->IsUndefined()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "src is not iterable", JSTaggedValue::Exception());
}
} else {
THROW_TYPE_ERROR_AND_RETURN(thread, "dst is not iterable", JSTaggedValue::Exception());
}
if (src->IsString()) {
JSHandle<EcmaString> srcString = JSTaggedValue::ToString(thread, src);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<EcmaString> srcFlat = JSHandle<EcmaString>(thread,
EcmaStringAccessor::Flatten(thread->GetEcmaVM(), srcString));
uint32_t dstLen = static_cast<uint32_t>(index.GetInt());
uint32_t strLen = EcmaStringAccessor(srcFlat).GetLength();
for (uint32_t i = 0; i < strLen; i++) {
uint16_t res = EcmaStringAccessor(srcFlat).Get<false>(i);
JSHandle<JSTaggedValue> strValue(factory->NewFromUtf16Literal(&res, 1));
JSTaggedValue::SetProperty(thread, dst, dstLen + i, strValue, true);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
JSHandle<JSTaggedValue> length(thread, JSTaggedValue(dstLen + strLen));
if (strLen == 0U) {
JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
JSTaggedValue::SetProperty(thread, dst, lengthKey, length);
}
return length.GetTaggedValue();
}
if (index.GetInt() == 0 && src->IsStableJSArray(thread)) {
JSHandle<TaggedArray> srcElements(thread, JSHandle<JSObject>::Cast(src)->GetElements());
uint32_t length = JSHandle<JSArray>::Cast(src)->GetArrayLength();
JSHandle<TaggedArray> dstElements = factory->NewTaggedArray(length);
JSHandle<JSArray> dstArray = JSHandle<JSArray>::Cast(dst);
dstArray->SetElements(thread, dstElements);
dstArray->SetArrayLength(thread, length);
JSHandle<JSObject> srcObj(src);
JSHandle<JSObject> dstObj(dst);
ElementAccessor::CopyJSArrayObject(thread, srcObj, dstObj, length);
for (uint32_t i = 0; i < length; i++) {
JSHandle<JSTaggedValue> reg(thread, ElementAccessor::Get(srcObj, i));
if (reg->IsHole()) {
JSHandle<JSTaggedValue> reg2(thread, JSArray::FastGetPropertyByValue(thread, src, i).GetTaggedValue());
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (reg2->IsHole()) {
ElementAccessor::Set(thread, dstObj, i, undefinedHandle, true);
} else {
ElementAccessor::Set(thread, dstObj, i, reg2, true);
}
} else {
ElementAccessor::Set(thread, dstObj, i, reg, true);
}
}
return JSTaggedValue(length);
}
JSHandle<JSTaggedValue> iter;
auto globalConst = thread->GlobalConstants();
if (src->IsJSArrayIterator() || src->IsJSMapIterator() || src->IsJSSetIterator() ||
src->IsIterator()) {
iter = src;
} else if (src->IsJSArray()) {
JSHandle<JSTaggedValue> valuesStr = globalConst->GetHandledValuesString();
JSHandle<JSTaggedValue> valuesMethod = JSObject::GetMethod(thread, src, valuesStr);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
iter = JSIterator::GetIterator(thread, src, valuesMethod);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
} else {
iter = JSIterator::GetIterator(thread, src);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
JSMutableHandle<JSTaggedValue> indexHandle(thread, index);
JSHandle<JSTaggedValue> valueStr = globalConst->GetHandledValueString();
PropertyDescriptor desc(thread);
JSHandle<JSTaggedValue> iterResult;
uint32_t srcLen = 0U;
do {
iterResult = JSIterator::IteratorStep(thread, iter);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (iterResult->IsFalse()) {
break;
}
bool success = JSTaggedValue::GetOwnProperty(thread, iterResult, valueStr, desc);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (success && desc.IsEnumerable()) {
JSTaggedValue::DefineOwnProperty(thread, dst, indexHandle, desc);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
int tmp = indexHandle->GetInt();
indexHandle.Update(JSTaggedValue(tmp + 1));
++srcLen;
}
} while (true);
if (srcLen == 0U) {
JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
JSTaggedValue::SetProperty(thread, dst, lengthKey, indexHandle);
}
return indexHandle.GetTaggedValue();
}
JSTaggedValue RuntimeStubs::RuntimeGetIteratorNext(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
const JSHandle<JSTaggedValue> &method)
{
ASSERT(method->IsCallable());
JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, method, obj, undefined, 0);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSTaggedValue ret = JSFunction::Call(info);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (!ret.IsECMAObject()) {
return RuntimeThrowTypeError(thread, "the Iterator is not an ecmaobject.");
}
return ret;
}
JSTaggedValue RuntimeStubs::RuntimeSetObjectWithProto(JSThread *thread, const JSHandle<JSTaggedValue> &proto,
const JSHandle<JSObject> &obj)
{
if (!proto->IsECMAObject() && !proto->IsNull()) {
return JSTaggedValue::False();
}
JSObject::SetPrototype(thread, obj, proto);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return JSTaggedValue::True();
}
JSTaggedValue RuntimeStubs::RuntimeLdObjByValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
const JSHandle<JSTaggedValue> &prop, bool callGetter,
JSTaggedValue receiver)
{
// Ecma Spec 2015 12.3.2.1
// 7. Let bv be RequireObjectCoercible(baseValue).
// 8. ReturnIfAbrupt(bv).
JSHandle<JSTaggedValue> object =
JSTaggedValue::RequireObjectCoercible(thread, obj, "Cannot load property of null or undefined");
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSTaggedValue res;
if (callGetter) {
res = JSObject::CallGetter(thread, AccessorData::Cast(receiver.GetTaggedObject()), object);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
} else {
JSHandle<JSTaggedValue> propKey = JSTaggedValue::ToPropertyKey(thread, prop);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
res = JSTaggedValue::GetProperty(thread, object, propKey).GetValue().GetTaggedValue();
}
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return res;
}
JSTaggedValue RuntimeStubs::RuntimeStObjByValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
const JSHandle<JSTaggedValue> &prop,
const JSHandle<JSTaggedValue> &value)
{
// Ecma Spec 2015 12.3.2.1
// 7. Let bv be RequireObjectCoercible(baseValue).
// 8. ReturnIfAbrupt(bv).
JSHandle<JSTaggedValue> object =
JSTaggedValue::RequireObjectCoercible(thread, obj, "Cannot store property of null or undefined");
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> propKey(JSTaggedValue::ToPropertyKey(thread, prop));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// strict mode is true
JSTaggedValue::SetProperty(thread, object, propKey, value, true);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return JSTaggedValue::True();
}
JSTaggedValue RuntimeStubs::RuntimeStOwnByValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
const JSHandle<JSTaggedValue> &key,
const JSHandle<JSTaggedValue> &value)
{
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
if (obj->IsClassConstructor() &&
JSTaggedValue::SameValue(key, globalConst->GetHandledPrototypeString())) {
return RuntimeThrowTypeError(thread, "In a class, static property named 'prototype' throw a TypeError");
}
// property in class is non-enumerable
bool enumerable = !(obj->IsClassPrototype() || obj->IsClassConstructor());
PropertyDescriptor desc(thread, value, true, enumerable, true);
JSHandle<JSTaggedValue> propKey = JSTaggedValue::ToPropertyKey(thread, key);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
bool ret = JSTaggedValue::DefineOwnProperty(thread, obj, propKey, desc);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (!ret) {
return RuntimeThrowTypeError(thread, "StOwnByValue failed");
}
return JSTaggedValue::True();
}
JSTaggedValue RuntimeStubs::RuntimeLdSuperByValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
const JSHandle<JSTaggedValue> &key, JSTaggedValue thisFunc)
{
ASSERT(thisFunc.IsJSFunction());
// get Homeobject form function
JSHandle<JSTaggedValue> homeObject(thread, JSFunction::Cast(thisFunc.GetTaggedObject())->GetHomeObject());
JSHandle<JSTaggedValue> superBase(thread, JSTaggedValue::GetSuperBase(thread, homeObject));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSTaggedValue::RequireObjectCoercible(thread, superBase);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> propKey(JSTaggedValue::ToPropertyKey(thread, key));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSTaggedValue res = JSTaggedValue::GetProperty(thread, superBase, propKey, obj).GetValue().GetTaggedValue();
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return res;
}
JSTaggedValue RuntimeStubs::RuntimeStSuperByValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
const JSHandle<JSTaggedValue> &key,
const JSHandle<JSTaggedValue> &value, JSTaggedValue thisFunc)
{
ASSERT(thisFunc.IsJSFunction());
// get Homeobject form function
JSHandle<JSTaggedValue> homeObject(thread, JSFunction::Cast(thisFunc.GetTaggedObject())->GetHomeObject());
JSHandle<JSTaggedValue> superBase(thread, JSTaggedValue::GetSuperBase(thread, homeObject));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSTaggedValue::RequireObjectCoercible(thread, superBase);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> propKey(JSTaggedValue::ToPropertyKey(thread, key));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// check may_throw is false?
JSTaggedValue::SetProperty(thread, superBase, propKey, value, obj, true);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return JSTaggedValue::True();
}
JSTaggedValue RuntimeStubs::RuntimeLdObjByIndex(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
uint32_t idx, bool callGetter, JSTaggedValue receiver)
{
JSTaggedValue res;
if (callGetter) {
res = JSObject::CallGetter(thread, AccessorData::Cast(receiver.GetTaggedObject()), obj);
} else {
res = JSTaggedValue::GetProperty(thread, obj, idx).GetValue().GetTaggedValue();
}
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return res;
}
JSTaggedValue RuntimeStubs::RuntimeLdObjByName(JSThread *thread, JSTaggedValue obj, JSTaggedValue prop,
bool callGetter, JSTaggedValue receiver)
{
JSHandle<JSTaggedValue> objHandle(thread, obj);
JSTaggedValue res;
if (callGetter) {
res = JSObject::CallGetter(thread, AccessorData::Cast(receiver.GetTaggedObject()), objHandle);
} else {
JSHandle<JSTaggedValue> propHandle(thread, prop);
res = JSTaggedValue::GetProperty(thread, objHandle, propHandle).GetValue().GetTaggedValue();
}
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return res;
}
JSTaggedValue RuntimeStubs::RuntimeStObjByName(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
const JSHandle<JSTaggedValue> &prop,
const JSHandle<JSTaggedValue> &value)
{
JSTaggedValue::SetProperty(thread, obj, prop, value, true);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return JSTaggedValue::True();
}
JSTaggedValue RuntimeStubs::RuntimeStObjByIndex(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
uint32_t idx, const JSHandle<JSTaggedValue> &value)
{
JSTaggedValue::SetProperty(thread, obj, idx, value, true);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return JSTaggedValue::True();
}
JSTaggedValue RuntimeStubs::RuntimeStOwnByIndex(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
const JSHandle<JSTaggedValue> &idx,
const JSHandle<JSTaggedValue> &value)
{
// property in class is non-enumerable
bool enumerable = !(obj->IsClassPrototype() || obj->IsClassConstructor());
PropertyDescriptor desc(thread, value, true, enumerable, true);
bool ret = JSTaggedValue::DefineOwnProperty(thread, obj, idx, desc);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (!ret) {
return RuntimeThrowTypeError(thread, "SetOwnByIndex failed");
}
return JSTaggedValue::True();
}
JSTaggedValue RuntimeStubs::RuntimeStGlobalRecord(JSThread *thread, const JSHandle<JSTaggedValue> &prop,
const JSHandle<JSTaggedValue> &value, bool isConst)
{
ObjectFactory* factory = thread->GetEcmaVM()->GetFactory();
if (thread->GetEcmaVM()->GetJSOptions().IsEnableLoweringBuiltin()) {
BuiltinIndex& builtinIndex = BuiltinIndex::GetInstance();
auto index = builtinIndex.GetBuiltinIndex(prop.GetTaggedValue());
if (index != BuiltinIndex::NOT_FOUND) {
auto box = factory->NewPropertyBox(JSHandle<JSTaggedValue>(thread, JSTaggedValue::Hole()));
thread->GetBuiltinEntriesPointer()->ClearByIndex(index, box.GetTaggedValue());
}
}
EcmaVM *vm = thread->GetEcmaVM();
JSHandle<GlobalEnv> env = vm->GetGlobalEnv();
GlobalDictionary *dict = GlobalDictionary::Cast(env->GetGlobalRecord()->GetTaggedObject());
// cross files global record name binding judgment
int entry = dict->FindEntry(prop.GetTaggedValue());
if (entry != -1) {
return RuntimeThrowSyntaxError(thread, "Duplicate identifier");
}
PropertyAttributes attributes;
if (isConst) {
attributes.SetIsConstProps(true);
}
JSHandle<GlobalDictionary> dictHandle(thread, dict);
JSHandle<PropertyBox> box = factory->NewPropertyBox(value);
PropertyBoxType boxType = value->IsUndefined() ? PropertyBoxType::UNDEFINED : PropertyBoxType::CONSTANT;
attributes.SetBoxType(boxType);
dict = *GlobalDictionary::PutIfAbsent(thread, dictHandle, prop, JSHandle<JSTaggedValue>(box), attributes);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
env->SetGlobalRecord(thread, JSTaggedValue(dict));
return JSTaggedValue::True();
}
JSTaggedValue RuntimeStubs::RuntimeNeg(JSThread *thread, const JSHandle<JSTaggedValue> &value)
{
JSHandle<JSTaggedValue> inputVal = JSTaggedValue::ToNumeric(thread, value);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (inputVal->IsBigInt()) {
JSHandle<BigInt> bigValue(inputVal);
return BigInt::UnaryMinus(thread, bigValue).GetTaggedValue();
}
JSTaggedNumber number(inputVal.GetTaggedValue());
if (number.IsInt()) {
int32_t intValue = number.GetInt();
if (intValue == 0) {
return JSTaggedValue(-0.0);
}
if (intValue == INT32_MIN) {
return JSTaggedValue(-static_cast<double>(INT32_MIN));
}
return JSTaggedValue(-intValue);
}
if (number.IsDouble()) {
return JSTaggedValue(-number.GetDouble());
}
LOG_ECMA(FATAL) << "this branch is unreachable";
UNREACHABLE();
}
JSTaggedValue RuntimeStubs::RuntimeNot(JSThread *thread, const JSHandle<JSTaggedValue> &value)
{
JSHandle<JSTaggedValue> inputVal = JSTaggedValue::ToNumeric(thread, value);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (inputVal->IsBigInt()) {
JSHandle<BigInt> bigValue(inputVal);
return BigInt::BitwiseNOT(thread, bigValue).GetTaggedValue();
}
int32_t number = JSTaggedValue::ToInt32(thread, inputVal);
return JSTaggedValue(~number); // NOLINT(hicpp-signed-bitwise)
}
JSTaggedValue RuntimeStubs::RuntimeResolveClass(JSThread *thread, const JSHandle<JSFunction> &ctor,
const JSHandle<TaggedArray> &literal,
const JSHandle<JSTaggedValue> &base,
const JSHandle<JSTaggedValue> &lexenv)
{
ASSERT(ctor.GetTaggedValue().IsClassConstructor());
FrameHandler frameHandler(thread);
JSTaggedValue currentFunc = frameHandler.GetFunction();
JSHandle<JSTaggedValue> ecmaModule(thread, JSFunction::Cast(currentFunc.GetTaggedObject())->GetModule());
RuntimeSetClassInheritanceRelationship(thread, JSHandle<JSTaggedValue>(ctor), base);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
uint32_t literalBufferLength = literal->GetLength();
ASSERT(literalBufferLength > 0);
// only traverse the value of key-value pair
for (uint32_t index = 1; index < literalBufferLength - 1; index += 2) { // 2: key-value pair
JSTaggedValue value = literal->Get(index);
if (LIKELY(value.IsJSFunction())) {
JSFunction *func = JSFunction::Cast(value.GetTaggedObject());
func->SetLexicalEnv(thread, lexenv.GetTaggedValue());
func->SetModule(thread, ecmaModule);
}
}
return ctor.GetTaggedValue();
}
JSTaggedValue RuntimeStubs::RuntimeCloneClassFromTemplate(JSThread *thread, const JSHandle<JSFunction> &ctor,
const JSHandle<JSTaggedValue> &base,
const JSHandle<JSTaggedValue> &lexenv)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
ASSERT(ctor.GetTaggedValue().IsClassConstructor());
JSHandle<JSObject> clsPrototype(thread, ctor->GetFunctionPrototype());
bool canShareHClass = false;
JSHandle<JSFunction> cloneClass = factory->CloneClassCtor(ctor, lexenv, canShareHClass);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSObject> cloneClassPrototype = factory->CloneObjectLiteral(JSHandle<JSObject>(clsPrototype), lexenv,
canShareHClass);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// After clone both, reset "constructor" and "prototype" properties.
JSFunction::SetFunctionPrototypeOrInstanceHClass(thread, cloneClass, cloneClassPrototype.GetTaggedValue());
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
PropertyDescriptor ctorDesc(thread, JSHandle<JSTaggedValue>(cloneClass), true, false, true);
JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle<JSTaggedValue>(cloneClassPrototype),
globalConst->GetHandledConstructorString(), ctorDesc);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
cloneClass->SetHomeObject(thread, cloneClassPrototype);
if (!canShareHClass) {
RuntimeSetClassInheritanceRelationship(thread, JSHandle<JSTaggedValue>(cloneClass), base);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
return cloneClass.GetTaggedValue();
}
bool RuntimeStubs::ShouldUseAOTHClass(const JSHandle<JSTaggedValue> &ihc,
const JSHandle<JSTaggedValue> &chc,
const JSHandle<ClassLiteral> &classLiteral)
{
return !(ihc->IsUndefined() || chc->IsUndefined() || classLiteral->GetIsAOTUsed());
}
// clone class may need re-set inheritance relationship due to extends may be a variable.
JSTaggedValue RuntimeStubs::RuntimeCreateClassWithBuffer(JSThread *thread,
const JSHandle<JSTaggedValue> &base,
const JSHandle<JSTaggedValue> &lexenv,
const JSHandle<JSTaggedValue> &constpool,
uint16_t methodId, uint16_t literalId,
const JSHandle<JSTaggedValue> &module,
const JSHandle<JSTaggedValue> &length)
{
if (base->IsJSShared()) {
THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(NotSendableSubClass), JSTaggedValue::Exception());
}
[[maybe_unused]] EcmaHandleScope handleScope(thread);
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
CString entry = ModuleManager::GetRecordName(module.GetTaggedValue());
// For class constructor.
auto methodObj = ConstantPool::GetMethodFromCache(thread, constpool.GetTaggedValue(), methodId);
JSHandle<JSTaggedValue> method(thread, methodObj);
JSHandle<ConstantPool> constpoolHandle = JSHandle<ConstantPool>::Cast(constpool);
JSHandle<JSFunction> cls;
JSMutableHandle<JSTaggedValue> ihc(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> chc(thread, JSTaggedValue::Undefined());
JSHandle<ConstantPool> cp(thread,
thread->GetCurrentEcmaContext()->FindOrCreateUnsharedConstpool(constpoolHandle.GetTaggedValue()));
JSTaggedValue val = cp->GetObjectFromCache(literalId);
if (val.IsAOTLiteralInfo()) {
JSHandle<AOTLiteralInfo> aotLiteralInfo(thread, val);
ihc.Update(aotLiteralInfo->GetIhc());
chc.Update(aotLiteralInfo->GetChc());
}
JSHandle<JSTaggedValue> sendableEnv(thread, JSTaggedValue::Undefined());
if (module->GetTaggedObject()->GetClass()->IsSourceTextModule()) {
JSHandle<SourceTextModule> moduleRecord = JSHandle<SourceTextModule>::Cast(module);
sendableEnv = JSHandle<JSTaggedValue>(thread, moduleRecord->GetSendableEnv());
}
JSTaggedValue literalObj = ConstantPool::GetClassLiteralFromCache(thread, cp, literalId, entry, sendableEnv);
JSHandle<ClassLiteral> classLiteral(thread, literalObj);
JSHandle<TaggedArray> arrayHandle(thread, classLiteral->GetArray());
JSHandle<ClassInfoExtractor> extractor = factory->NewClassInfoExtractor(method);
auto literalLength = arrayHandle->GetLength();
ClassInfoExtractor::BuildClassInfoExtractorFromLiteral(thread, extractor, arrayHandle, literalLength);
if (ShouldUseAOTHClass(ihc, chc, classLiteral)) {
classLiteral->SetIsAOTUsed(true);
JSHandle<JSHClass> chclass(chc);
cls = ClassHelper::DefineClassWithIHClass(thread, extractor,
lexenv, ihc, chclass);
} else {
cls = ClassHelper::DefineClassFromExtractor(thread, base, extractor, lexenv);
}
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
RuntimeSetClassInheritanceRelationship(thread, JSHandle<JSTaggedValue>(cls), base);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
cls->SetLexicalEnv(thread, lexenv.GetTaggedValue());
cls->SetModule(thread, module.GetTaggedValue());
const JSHandle<Method> methodHandle(thread, methodObj);
QuickFixHelper::SetPatchModule(thread, methodHandle, cls);
RuntimeSetClassConstructorLength(thread, cls.GetTaggedValue(), length.GetTaggedValue());
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return cls.GetTaggedValue();
}
JSTaggedValue RuntimeStubs::RuntimeCreateSharedClass(JSThread *thread,
const JSHandle<JSTaggedValue> &base,
const JSHandle<JSTaggedValue> &constpool,
uint16_t methodId, uint16_t literalId,
uint16_t length, const JSHandle<JSTaggedValue> &module)
{
if (!base->IsJSShared() && !base->IsHole()) {
THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(ClassNotDerivedFromShared), JSTaggedValue::Exception());
}
[[maybe_unused]] EcmaHandleScope handleScope(thread);
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
CString entry = ModuleManager::GetRecordName(module.GetTaggedValue());
auto methodObj = ConstantPool::GetMethodFromCache(thread, constpool.GetTaggedValue(), methodId);
JSHandle<JSTaggedValue> method(thread, methodObj);
JSHandle<ConstantPool> constpoolHandle = JSHandle<ConstantPool>::Cast(constpool);
JSHandle<JSTaggedValue> sendableEnv(thread, JSTaggedValue::Undefined());
if (module->GetTaggedObject()->GetClass()->IsSourceTextModule()) {
JSHandle<SourceTextModule> moduleRecord = JSHandle<SourceTextModule>::Cast(module);
sendableEnv = JSHandle<JSTaggedValue>(thread, moduleRecord->GetSendableEnv());
}
auto literalObj = ConstantPool::GetClassLiteralFromCache(
thread, constpoolHandle, literalId, entry, sendableEnv, ClassKind::SENDABLE);
JSHandle<ClassLiteral> classLiteral(thread, literalObj);
JSHandle<TaggedArray> arrayHandle(thread, classLiteral->GetArray());
auto literalLength = arrayHandle->GetLength();
ASSERT(literalLength > 0);
// fieldTypeId is the last element in literal buffer
auto fieldTypeId = static_cast<uint32_t>(arrayHandle->Get(literalLength - 1).GetInt());
// Don't trim array, because define class maybe called on muilt-time in the same vm or diferrent vm
JSHandle<ClassInfoExtractor> extractor = factory->NewClassInfoExtractor(method);
ClassInfoExtractor::BuildClassInfoExtractorFromLiteral(thread, extractor, arrayHandle,
literalLength - 1, ClassKind::SENDABLE);
JSHandle<TaggedArray> fieldTypeArray = ConstantPool::GetFieldLiteral(thread, constpoolHandle, fieldTypeId, entry);
JSHandle<TaggedArray> staticFieldArray = SendableClassDefiner::ExtractStaticFieldTypeArray(thread, fieldTypeArray);
JSHandle<JSFunction> cls =
SendableClassDefiner::DefineSendableClassFromExtractor(thread, extractor, staticFieldArray);
ModuleManager *moduleManager = thread->GetCurrentEcmaContext()->GetModuleManager();
JSHandle<JSTaggedValue> sendableClsModule = moduleManager->GenerateSendableFuncModule(module);
JSHandle<SourceTextModule> sendableClsModuleRecord(sendableClsModule);
sendableClsModuleRecord->SetSendableEnv(thread, sendableEnv);
cls->SetModule(thread, sendableClsModule.GetTaggedValue());
RuntimeSetClassConstructorLength(thread, cls.GetTaggedValue(), JSTaggedValue(length));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
RuntimeSetClassInheritanceRelationship(thread, JSHandle<JSTaggedValue>(cls), base, ClassKind::SENDABLE);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
uint32_t arrayLength = fieldTypeArray->GetLength();
auto instanceFieldNums = static_cast<uint32_t>(fieldTypeArray->Get(arrayLength - 1).GetInt());
// Don't trim array, because define class maybe called on muilt-time in the same vm or diferrent vm
uint32_t instanceLength = instanceFieldNums * 2; // 2: key and value
ASSERT(instanceLength < arrayLength);
SendableClassDefiner::DefineSendableInstanceHClass(thread, fieldTypeArray, instanceLength, cls, base);
return cls.GetTaggedValue();
}
JSTaggedValue RuntimeStubs::RuntimeSetClassInheritanceRelationship(JSThread *thread,
const JSHandle<JSTaggedValue> &ctor,
const JSHandle<JSTaggedValue> &base,
ClassKind kind)
{
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
ASSERT(ctor->IsJSFunction());
JSHandle<JSTaggedValue> parent = base;
/*
* class A / class A extends null class A extends B
* a a
* | |
* | __proto__ | __proto__
* | |
* A ----> A.prototype A ----> A.prototype
* | | | |
* | __proto__ | __proto__ | __proto__ | __proto__
* | | | |
* Function.prototype Object.prototype / null B ----> B.prototype
*/
JSHandle<JSTaggedValue> parentPrototype;
// hole means parent is not present
Method *method = Method::Cast(JSHandle<JSFunction>::Cast(ctor)->GetMethod().GetTaggedObject());
if (parent->IsHole()) {
method->SetFunctionKind(FunctionKind::CLASS_CONSTRUCTOR);
parentPrototype =
(kind == ClassKind::SENDABLE) ? env->GetSObjectFunctionPrototype() : env->GetObjectFunctionPrototype();
parent = (kind == ClassKind::SENDABLE) ? env->GetSFunctionPrototype() : env->GetFunctionPrototype();
} else if (parent->IsNull()) {
method->SetFunctionKind(FunctionKind::DERIVED_CONSTRUCTOR);
parentPrototype = JSHandle<JSTaggedValue>(thread, JSTaggedValue::Null());
parent = (kind == ClassKind::SENDABLE) ? env->GetSFunctionPrototype() : env->GetFunctionPrototype();
} else if (!parent->IsConstructor()) {
return RuntimeThrowTypeError(thread, "parent class is not constructor");
} else {
method->SetFunctionKind(FunctionKind::DERIVED_CONSTRUCTOR);
parentPrototype = JSTaggedValue::GetProperty(thread, parent,
globalConst->GetHandledPrototypeString()).GetValue();
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (!parentPrototype->IsECMAObject() && !parentPrototype->IsNull()) {
return RuntimeThrowTypeError(thread, "parent class have no valid prototype");
}
}
ctor->GetTaggedObject()->GetClass()->SetPrototype(thread, parent); // __proto__
JSHandle<JSObject> clsPrototype(thread, JSHandle<JSFunction>(ctor)->GetFunctionPrototype());
clsPrototype->GetClass()->SetPrototype(thread, parentPrototype);
// ctor -> hclass -> EnableProtoChangeMarker
auto constructor = JSFunction::Cast(ctor.GetTaggedValue().GetTaggedObject());
if (constructor->GetClass()->IsTS()) {
JSHClass::EnableProtoChangeMarker(thread, JSHandle<JSHClass>(thread, constructor->GetClass()));
// prototype -> hclass -> EnableProtoChangeMarker
JSHClass::EnableProtoChangeMarker(thread,
JSHandle<JSHClass>(thread, constructor->GetFunctionPrototype().GetTaggedObject()->GetClass()));
}
// by enableing the ProtoChangeMarker, the IHC generated in the Aot stage
// is registered into the listener of its prototype. In this way, it is ensured
// that when the prototype changes, the affected IHC can be notified.
JSTaggedValue protoOrHClass = JSHandle<JSFunction>(ctor)->GetProtoOrHClass();
if (protoOrHClass.IsJSHClass()) {
JSHClass *ihc = JSHClass::Cast(protoOrHClass.GetTaggedObject());
if (ihc->IsTS()) {
JSHandle<JSHClass> ihcHandle(thread, ihc);
JSHClass::EnableProtoChangeMarker(thread, ihcHandle);
}
} else {
JSHandle<JSObject> protoHandle(thread, protoOrHClass);
if (protoHandle->GetJSHClass()->IsTS()) {
JSHClass::EnablePHCProtoChangeMarker(thread, JSHandle<JSHClass>(thread, protoHandle->GetJSHClass()));
}
}
return JSTaggedValue::Undefined();
}
JSTaggedValue RuntimeStubs::RuntimeSetClassConstructorLength(JSThread *thread, JSTaggedValue ctor,
JSTaggedValue length)
{
ASSERT(ctor.IsClassConstructor());
JSFunction* cls = JSFunction::Cast(ctor.GetTaggedObject());
if (LIKELY(!cls->GetClass()->IsDictionaryMode())) {
cls->SetPropertyInlinedProps(thread, JSFunction::LENGTH_INLINE_PROPERTY_INDEX, length);
} else {
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
cls->UpdatePropertyInDictionary(thread, globalConst->GetLengthString(), length);
}
return JSTaggedValue::Undefined();
}
JSTaggedValue RuntimeStubs::RuntimeNotifyInlineCache(JSThread *thread, const JSHandle<JSFunction> &function,
uint32_t icSlotSize)
{
if (icSlotSize == 0) {
return JSTaggedValue::Undefined();
}
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<ProfileTypeInfo> profileTypeInfo;
if (function->GetClass()->IsJSSharedFunction()) {
return JSTaggedValue::Undefined();
} else {
profileTypeInfo = factory->NewProfileTypeInfo(icSlotSize);
}
// overflow 8bit
if (icSlotSize > ProfileTypeInfo::INVALID_SLOT_INDEX) {
// set as mega
profileTypeInfo->Set(thread, ProfileTypeInfo::INVALID_SLOT_INDEX, JSTaggedValue::Hole());
ASSERT(icSlotSize <= ProfileTypeInfo::MAX_SLOT_INDEX + 1);
}
JSFunction::SetProfileTypeInfo(thread, function, JSHandle<JSTaggedValue>::Cast(profileTypeInfo));
return profileTypeInfo.GetTaggedValue();
}
JSTaggedValue RuntimeStubs::RuntimeStOwnByValueWithNameSet(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
const JSHandle<JSTaggedValue> &key,
const JSHandle<JSTaggedValue> &value)
{
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
if (obj->IsClassConstructor() &&
JSTaggedValue::SameValue(key, globalConst->GetHandledPrototypeString())) {
return RuntimeThrowTypeError(thread, "In a class, static property named 'prototype' throw a TypeError");
}
// property in class is non-enumerable
bool enumerable = !(obj->IsClassPrototype() || obj->IsClassConstructor());
PropertyDescriptor desc(thread, value, true, enumerable, true);
JSHandle<JSTaggedValue> propKey = JSTaggedValue::ToPropertyKey(thread, key);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
bool ret = JSTaggedValue::DefineOwnProperty(thread, obj, propKey, desc);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (!ret) {
return RuntimeThrowTypeError(thread, "StOwnByValueWithNameSet failed");
}
if (value->IsJSFunction()) {
if (propKey->IsNumber()) {
propKey = JSHandle<JSTaggedValue>(base::NumberHelper::NumberToString(thread, propKey.GetTaggedValue()));
}
JSFunctionBase::SetFunctionName(thread, JSHandle<JSFunctionBase>::Cast(value), propKey,
JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
}
return JSTaggedValue::True();
}
JSTaggedValue RuntimeStubs::RuntimeStOwnByName(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
const JSHandle<JSTaggedValue> &prop,
const JSHandle<JSTaggedValue> &value)
{
ASSERT(prop->IsStringOrSymbol());
// property in class is non-enumerable
bool enumerable = !(obj->IsClassPrototype() || obj->IsClassConstructor());
PropertyDescriptor desc(thread, value, true, enumerable, true);
bool ret = JSTaggedValue::DefineOwnProperty(thread, obj, prop, desc);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (!ret) {
return RuntimeThrowTypeError(thread, "SetOwnByName failed");
}
return JSTaggedValue::True();
}
JSTaggedValue RuntimeStubs::RuntimeStOwnByNameWithNameSet(JSThread *thread,
const JSHandle<JSTaggedValue> &objHandle,
const JSHandle<JSTaggedValue> &propHandle,
const JSHandle<JSTaggedValue> &valueHandle)
{
ASSERT(propHandle->IsStringOrSymbol());
JSHandle<JSTaggedValue> propKey = JSTaggedValue::ToPropertyKey(thread, propHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// property in class is non-enumerable
bool enumerable = !(objHandle->IsClassPrototype() || objHandle->IsClassConstructor());
PropertyDescriptor desc(thread, valueHandle, true, enumerable, true);
bool ret = JSTaggedValue::DefineOwnProperty(thread, objHandle, propHandle, desc);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (!ret) {
return RuntimeThrowTypeError(thread, "SetOwnByNameWithNameSet failed");
}
JSFunctionBase::SetFunctionName(thread, JSHandle<JSFunctionBase>::Cast(valueHandle), propKey,
JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
return JSTaggedValue::True();
}
JSTaggedValue RuntimeStubs::RuntimeSuspendGenerator(JSThread *thread, const JSHandle<JSTaggedValue> &genObj,
const JSHandle<JSTaggedValue> &value)
{
if (genObj->IsAsyncGeneratorObject()) {
JSHandle<JSAsyncGeneratorObject> generatorObjectHandle(genObj);
JSHandle<GeneratorContext> genContextHandle(thread, generatorObjectHandle->GetGeneratorContext());
// save stack, should copy cur_frame, function execute over will free cur_frame
SaveFrameToContext(thread, genContextHandle);
// change state to SuspendedYield
if (generatorObjectHandle->IsExecuting()) {
return value.GetTaggedValue();
}
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return generatorObjectHandle.GetTaggedValue();
}
if (genObj->IsGeneratorObject()) {
JSHandle<JSGeneratorObject> generatorObjectHandle(genObj);
JSHandle<GeneratorContext> genContextHandle(thread, generatorObjectHandle->GetGeneratorContext());
// set TaskInfo for TaskPool
generatorObjectHandle->SetTaskInfo(thread->GetTaskInfo());
// save stack, should copy cur_frame, function execute over will free cur_frame
SaveFrameToContext(thread, genContextHandle);
// change state to SuspendedYield
if (generatorObjectHandle->IsExecuting()) {
generatorObjectHandle->SetGeneratorState(JSGeneratorState::SUSPENDED_YIELD);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return value.GetTaggedValue();
}
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return generatorObjectHandle.GetTaggedValue();
}
return RuntimeThrowTypeError(thread, "RuntimeSuspendGenerator failed");
}
void RuntimeStubs::RuntimeSetGeneratorState(JSThread *thread, const JSHandle<JSTaggedValue> &genObj,
const int32_t index)
{
JSHandle<JSAsyncGeneratorObject> generatorObjectHandle(genObj);
JSHandle<GeneratorContext> genContextHandle(thread, generatorObjectHandle->GetGeneratorContext());
// change state
switch (index) {
case static_cast<int32_t>(JSAsyncGeneratorState::SUSPENDED_START):
generatorObjectHandle->SetAsyncGeneratorState(JSAsyncGeneratorState::SUSPENDED_START);
break;
case static_cast<int32_t>(JSAsyncGeneratorState::SUSPENDED_YIELD):
generatorObjectHandle->SetAsyncGeneratorState(JSAsyncGeneratorState::SUSPENDED_YIELD);
break;
case static_cast<int32_t>(JSAsyncGeneratorState::EXECUTING):
generatorObjectHandle->SetAsyncGeneratorState(JSAsyncGeneratorState::EXECUTING);
break;
case static_cast<int32_t>(JSAsyncGeneratorState::COMPLETED):
generatorObjectHandle->SetAsyncGeneratorState(JSAsyncGeneratorState::COMPLETED);
break;
case static_cast<int32_t>(JSAsyncGeneratorState::AWAITING_RETURN):
generatorObjectHandle->SetAsyncGeneratorState(JSAsyncGeneratorState::AWAITING_RETURN);
break;
default:
LOG_ECMA(FATAL) << "this branch is unreachable";
UNREACHABLE();
}
}
JSTaggedValue RuntimeStubs::RuntimeGetModuleNamespace(JSThread *thread, int32_t index)
{
return thread->GetCurrentEcmaContext()->GetModuleManager()->GetModuleNamespace(index);
}
JSTaggedValue RuntimeStubs::RuntimeGetModuleNamespace(JSThread *thread, int32_t index,
JSTaggedValue jsFunc)
{
return thread->GetCurrentEcmaContext()->GetModuleManager()->GetModuleNamespace(index, jsFunc);
}
JSTaggedValue RuntimeStubs::RuntimeGetModuleNamespace(JSThread *thread, JSTaggedValue localName)
{
return thread->GetCurrentEcmaContext()->GetModuleManager()->GetModuleNamespace(localName);
}
JSTaggedValue RuntimeStubs::RuntimeGetModuleNamespace(JSThread *thread, JSTaggedValue localName,
JSTaggedValue jsFunc)
{
return thread->GetCurrentEcmaContext()->GetModuleManager()->GetModuleNamespace(localName, jsFunc);
}
void RuntimeStubs::RuntimeStModuleVar(JSThread *thread, int32_t index, JSTaggedValue value)
{
thread->GetCurrentEcmaContext()->GetModuleManager()->StoreModuleValue(index, value);
}
void RuntimeStubs::RuntimeStModuleVar(JSThread *thread, int32_t index, JSTaggedValue value,
JSTaggedValue jsFunc)
{
thread->GetCurrentEcmaContext()->GetModuleManager()->StoreModuleValue(index, value, jsFunc);
}
void RuntimeStubs::RuntimeStModuleVar(JSThread *thread, JSTaggedValue key, JSTaggedValue value)
{
thread->GetCurrentEcmaContext()->GetModuleManager()->StoreModuleValue(key, value);
}
void RuntimeStubs::RuntimeStModuleVar(JSThread *thread, JSTaggedValue key, JSTaggedValue value,
JSTaggedValue jsFunc)
{
thread->GetCurrentEcmaContext()->GetModuleManager()->StoreModuleValue(key, value, jsFunc);
}
JSTaggedValue RuntimeStubs::RuntimeLdLocalModuleVar(JSThread *thread, int32_t index)
{
return thread->GetCurrentEcmaContext()->GetModuleManager()->GetModuleValueInner(index);
}
JSTaggedValue RuntimeStubs::RuntimeLdLocalModuleVar(JSThread *thread, int32_t index, JSTaggedValue jsFunc)
{
return thread->GetCurrentEcmaContext()->GetModuleManager()->GetModuleValueInner(index, jsFunc);
}
JSTaggedValue RuntimeStubs::RuntimeLdExternalModuleVar(JSThread *thread, int32_t index)
{
return thread->GetCurrentEcmaContext()->GetModuleManager()->GetModuleValueOutter(index);
}
JSTaggedValue RuntimeStubs::RuntimeLdSendableExternalModuleVar(JSThread *thread, int32_t index, JSTaggedValue jsFunc)
{
return SharedModuleManager::GetInstance()->GetSendableModuleValue(thread, index, jsFunc);
}
JSTaggedValue RuntimeStubs::RuntimeLdExternalModuleVar(JSThread *thread, int32_t index, JSTaggedValue jsFunc)
{
return thread->GetCurrentEcmaContext()->GetModuleManager()->GetModuleValueOutter(index, jsFunc);
}
JSTaggedValue RuntimeStubs::RuntimeLdModuleVar(JSThread *thread, JSTaggedValue key, bool inner)
{
if (inner) {
JSTaggedValue moduleValue = thread->GetCurrentEcmaContext()->GetModuleManager()->GetModuleValueInner(key);
return moduleValue;
}
return thread->GetCurrentEcmaContext()->GetModuleManager()->GetModuleValueOutter(key);
}
JSTaggedValue RuntimeStubs::RuntimeLdModuleVar(JSThread *thread, JSTaggedValue key, bool inner,
JSTaggedValue jsFunc)
{
if (inner) {
JSTaggedValue moduleValue =
thread->GetCurrentEcmaContext()->GetModuleManager()->GetModuleValueInner(key, jsFunc);
return moduleValue;
}
return thread->GetCurrentEcmaContext()->GetModuleManager()->GetModuleValueOutter(key, jsFunc);
}
JSTaggedValue RuntimeStubs::RuntimeGetPropIterator(JSThread *thread, const JSHandle<JSTaggedValue> &value)
{
JSHandle<JSForInIterator> iteratorHandle = JSObject::EnumerateObjectProperties(thread, value);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return iteratorHandle.GetTaggedValue();
}
JSTaggedValue RuntimeStubs::RuntimeAsyncFunctionEnter(JSThread *thread)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
// 1. create promise
JSHandle<GlobalEnv> globalEnv = thread->GetEcmaVM()->GetGlobalEnv();
JSHandle<JSFunction> promiseFunc(globalEnv->GetPromiseFunction());
JSHandle<JSPromise> promiseObject(factory->NewJSObjectByConstructor(promiseFunc));
promiseObject->SetPromiseState(PromiseState::PENDING);
// 2. create asyncfuncobj
JSHandle<JSAsyncFuncObject> asyncFuncObj = factory->NewJSAsyncFuncObject();
asyncFuncObj->SetPromise(thread, promiseObject);
JSHandle<GeneratorContext> context = factory->NewGeneratorContext();
context->SetGeneratorObject(thread, asyncFuncObj);
// change state to EXECUTING
asyncFuncObj->SetGeneratorState(JSGeneratorState::EXECUTING);
asyncFuncObj->SetGeneratorContext(thread, context);
// 3. return asyncfuncobj
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return asyncFuncObj.GetTaggedValue();
}
JSTaggedValue RuntimeStubs::RuntimeGetIterator(JSThread *thread, const JSHandle<JSTaggedValue> &obj)
{
EcmaVM *vm = thread->GetEcmaVM();
JSHandle<GlobalEnv> env = vm->GetGlobalEnv();
JSHandle<JSTaggedValue> valuesFunc =
JSTaggedValue::GetProperty(thread, obj, env->GetIteratorSymbol()).GetValue();
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (!valuesFunc->IsCallable()) {
return valuesFunc.GetTaggedValue();
}
JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, valuesFunc, obj, undefined, 0);
return EcmaInterpreter::Execute(info);
}
JSTaggedValue RuntimeStubs::RuntimeGetAsyncIterator(JSThread *thread, const JSHandle<JSTaggedValue> &obj)
{
JSHandle<JSTaggedValue> asyncit = JSIterator::GetAsyncIterator(thread, obj);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return asyncit.GetTaggedValue();
}
JSTaggedValue RuntimeStubs::RuntimeLdPrivateProperty(JSThread *thread, JSTaggedValue lexicalEnv,
uint32_t levelIndex, uint32_t slotIndex, JSTaggedValue obj)
{
JSTaggedValue currentLexicalEnv = lexicalEnv;
for (uint32_t i = 0; i < levelIndex; i++) {
currentLexicalEnv = LexicalEnv::Cast(currentLexicalEnv.GetTaggedObject())->GetParentEnv();
ASSERT(!currentLexicalEnv.IsUndefined());
}
JSTaggedValue key = LexicalEnv::Cast(currentLexicalEnv.GetTaggedObject())->GetProperties(slotIndex);
// private property is invisible for proxy
JSHandle<JSTaggedValue> handleObj(thread, obj.IsJSProxy() ? JSProxy::Cast(obj)->GetPrivateField() : obj);
JSHandle<JSTaggedValue> handleKey(thread, key);
if (handleKey->IsJSFunction()) { // getter
JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
// 0: getter has 0 arg
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, handleKey, handleObj, undefined, 0);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSTaggedValue resGetter = JSFunction::Call(info);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return resGetter;
}
PropertyDescriptor desc(thread);
if (!JSTaggedValue::IsPropertyKey(handleKey) ||
!JSTaggedValue::GetOwnProperty(thread, handleObj, handleKey, desc)) {
THROW_TYPE_ERROR_AND_RETURN(thread, "invalid or cannot find private key", JSTaggedValue::Exception());
}
JSTaggedValue res = desc.GetValue().GetTaggedValue();
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return res;
}
JSTaggedValue RuntimeStubs::RuntimeStPrivateProperty(JSThread *thread, JSTaggedValue lexicalEnv,
uint32_t levelIndex, uint32_t slotIndex, JSTaggedValue obj, JSTaggedValue value)
{
JSTaggedValue currentLexicalEnv = lexicalEnv;
for (uint32_t i = 0; i < levelIndex; i++) {
currentLexicalEnv = LexicalEnv::Cast(currentLexicalEnv.GetTaggedObject())->GetParentEnv();
ASSERT(!currentLexicalEnv.IsUndefined());
}
JSTaggedValue key = LexicalEnv::Cast(currentLexicalEnv.GetTaggedObject())->GetProperties(slotIndex);
// private property is invisible for proxy
JSHandle<JSTaggedValue> handleObj(thread, obj.IsJSProxy() ? JSProxy::Cast(obj)->GetPrivateField() : obj);
JSHandle<JSTaggedValue> handleKey(thread, key);
JSHandle<JSTaggedValue> handleValue(thread, value);
if (handleKey->IsJSFunction()) { // setter
JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
// 1: setter has 1 arg
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, handleKey, handleObj, undefined, 1);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(handleValue.GetTaggedValue());
JSTaggedValue resSetter = JSFunction::Call(info);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return resSetter;
}
PropertyDescriptor desc(thread);
if (!JSTaggedValue::IsPropertyKey(handleKey) ||
!JSTaggedValue::GetOwnProperty(thread, handleObj, handleKey, desc)) {
THROW_TYPE_ERROR_AND_RETURN(thread, "invalid or cannot find private key", JSTaggedValue::Exception());
}
desc.SetValue(handleValue);
bool res = JSTaggedValue::DefineOwnProperty(thread, handleObj, handleKey, desc);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return JSTaggedValue(res);
}
JSTaggedValue RuntimeStubs::RuntimeTestIn(JSThread *thread, JSTaggedValue lexicalEnv,
uint32_t levelIndex, uint32_t slotIndex, JSTaggedValue obj)
{
if (!obj.IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot use 'in' operator in Non-Object", JSTaggedValue::Exception());
}
JSTaggedValue currentLexicalEnv = lexicalEnv;
for (uint32_t i = 0; i < levelIndex; i++) {
currentLexicalEnv = LexicalEnv::Cast(currentLexicalEnv.GetTaggedObject())->GetParentEnv();
ASSERT(!currentLexicalEnv.IsUndefined());
}
JSTaggedValue key = LexicalEnv::Cast(currentLexicalEnv.GetTaggedObject())->GetProperties(slotIndex);
JSHandle<JSTaggedValue> handleObj(thread, obj.IsJSProxy() ? JSProxy::Cast(obj)->GetPrivateField() : obj);
JSHandle<JSTaggedValue> handleKey(thread, key);
bool res = JSTaggedValue::IsPropertyKey(handleKey) && JSTaggedValue::HasProperty(thread, handleObj, handleKey);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return JSTaggedValue(res);
}
void RuntimeStubs::RuntimeThrow(JSThread *thread, JSTaggedValue value)
{
thread->SetException(value);
}
void RuntimeStubs::RuntimeThrowThrowNotExists(JSThread *thread)
{
THROW_TYPE_ERROR(thread, "Throw method is not defined");
}
void RuntimeStubs::RuntimeThrowPatternNonCoercible(JSThread *thread)
{
JSHandle<EcmaString> msg(thread->GlobalConstants()->GetHandledObjNotCoercibleString());
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
THROW_NEW_ERROR_AND_RETURN(thread,
factory->NewJSError(base::ErrorType::TYPE_ERROR, msg, StackCheck::NO).GetTaggedValue());
}
void RuntimeStubs::RuntimeThrowDeleteSuperProperty(JSThread *thread)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<EcmaString> info = factory->NewFromASCII("Can not delete super property");
JSHandle<JSObject> errorObj = factory->NewJSError(base::ErrorType::REFERENCE_ERROR, info, StackCheck::NO);
THROW_NEW_ERROR_AND_RETURN(thread, errorObj.GetTaggedValue());
}
void RuntimeStubs::RuntimeThrowUndefinedIfHole(JSThread *thread, const JSHandle<EcmaString> &obj)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<EcmaString> info = factory->NewFromASCII(" is not initialized");
JSHandle<EcmaString> msg = factory->ConcatFromString(obj, info);
THROW_NEW_ERROR_AND_RETURN(thread, factory->NewJSError(base::ErrorType::REFERENCE_ERROR,
msg, StackCheck::NO).GetTaggedValue());
}
void RuntimeStubs::RuntimeThrowIfNotObject(JSThread *thread)
{
THROW_TYPE_ERROR(thread, "Inner return result is not object");
}
void RuntimeStubs::RuntimeThrowConstAssignment(JSThread *thread, const JSHandle<EcmaString> &value)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<EcmaString> info = factory->NewFromASCII("Assignment to const variable ");
JSHandle<EcmaString> msg = factory->ConcatFromString(info, value);
THROW_NEW_ERROR_AND_RETURN(thread, factory->NewJSError(base::ErrorType::TYPE_ERROR,
msg, StackCheck::NO).GetTaggedValue());
}
JSTaggedValue RuntimeStubs::RuntimeLdGlobalRecord(JSThread *thread, JSTaggedValue key)
{
EcmaVM *vm = thread->GetEcmaVM();
JSHandle<GlobalEnv> env = vm->GetGlobalEnv();
GlobalDictionary *dict = GlobalDictionary::Cast(env->GetGlobalRecord()->GetTaggedObject());
int entry = dict->FindEntry(key);
if (entry != -1) {
return JSTaggedValue(dict->GetBox(entry));
}
return JSTaggedValue::Undefined();
}
JSTaggedValue RuntimeStubs::RuntimeTryLdGlobalByName(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
const JSHandle<JSTaggedValue> &prop)
{
OperationResult res = JSTaggedValue::GetProperty(thread, obj, prop);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (!res.GetPropertyMetaData().IsFound()) {
return RuntimeThrowReferenceError(thread, prop, " is not defined");
}
return res.GetValue().GetTaggedValue();
}
JSTaggedValue RuntimeStubs::RuntimeTryUpdateGlobalRecord(JSThread *thread, JSTaggedValue prop,
JSTaggedValue value)
{
EcmaVM *vm = thread->GetEcmaVM();
JSHandle<GlobalEnv> env = vm->GetGlobalEnv();
GlobalDictionary *dict = GlobalDictionary::Cast(env->GetGlobalRecord()->GetTaggedObject());
int entry = dict->FindEntry(prop);
ASSERT(entry != -1);
if (dict->GetAttributes(entry).IsConstProps()) {
return RuntimeThrowTypeError(thread, "const variable can not be modified");
}
PropertyBox *box = dict->GetBox(entry);
box->SetValue(thread, value);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return JSTaggedValue::True();
}
JSTaggedValue RuntimeStubs::RuntimeThrowReferenceError(JSThread *thread, const JSHandle<JSTaggedValue> &prop,
const char *desc)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<EcmaString> propName = JSTaggedValue::ToString(thread, prop);
ASSERT_NO_ABRUPT_COMPLETION(thread);
JSHandle<EcmaString> info = factory->NewFromUtf8(desc);
JSHandle<EcmaString> msg = factory->ConcatFromString(propName, info);
THROW_NEW_ERROR_AND_RETURN_VALUE(thread,
factory->NewJSError(base::ErrorType::REFERENCE_ERROR, msg, StackCheck::NO).GetTaggedValue(),
JSTaggedValue::Exception());
}
JSTaggedValue RuntimeStubs::RuntimeLdGlobalVarFromProto(JSThread *thread, const JSHandle<JSTaggedValue> &globalObj,
const JSHandle<JSTaggedValue> &prop)
{
ASSERT(globalObj->IsJSGlobalObject());
JSHandle<JSObject> global(globalObj);
JSHandle<JSTaggedValue> obj(thread, JSObject::GetPrototype(global));
OperationResult res = JSTaggedValue::GetProperty(thread, obj, prop);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return res.GetValue().GetTaggedValue();
}
JSTaggedValue RuntimeStubs::RuntimeStGlobalVar(JSThread *thread, const JSHandle<JSTaggedValue> &prop,
const JSHandle<JSTaggedValue> &value)
{
JSHandle<JSTaggedValue> global(thread, thread->GetEcmaVM()->GetGlobalEnv()->GetGlobalObject());
JSObject::GlobalSetProperty(thread, prop, value, true);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return JSTaggedValue::True();
}
JSTaggedValue RuntimeStubs::RuntimeToNumber(JSThread *thread, const JSHandle<JSTaggedValue> &value)
{
return JSTaggedValue::ToNumber(thread, value);
}
JSTaggedValue RuntimeStubs::RuntimeToNumeric(JSThread *thread, const JSHandle<JSTaggedValue> &value)
{
return JSTaggedValue::ToNumeric(thread, value).GetTaggedValue();
}
JSTaggedValue RuntimeStubs::RuntimeDynamicImport(JSThread *thread, const JSHandle<JSTaggedValue> &specifier,
const JSHandle<JSTaggedValue> &func)
{
EcmaVM *ecmaVm = thread->GetEcmaVM();
JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
ObjectFactory *factory = ecmaVm->GetFactory();
// get current filename
JSFunction *function = JSFunction::Cast(func->GetTaggedObject());
const JSPandaFile *jsPandaFile = function->GetCallTarget()->GetJSPandaFile();
CString currentfilename = jsPandaFile->GetJSPandaFileDesc();
JSMutableHandle<JSTaggedValue> dirPath(thread, thread->GlobalConstants()->GetUndefined());
JSMutableHandle<JSTaggedValue> recordName(thread, thread->GlobalConstants()->GetUndefined());
if (jsPandaFile->IsBundlePack()) {
dirPath.Update(factory->NewFromUtf8(currentfilename).GetTaggedValue());
} else {
recordName.Update(function->GetRecordName());
dirPath.Update(factory->NewFromUtf8(currentfilename).GetTaggedValue());
}
// 4. Let promiseCapability be !NewPromiseCapability(%Promise%).
JSHandle<JSTaggedValue> promiseFunc = env->GetPromiseFunction();
JSHandle<PromiseCapability> promiseCapability = JSPromise::NewPromiseCapability(thread, promiseFunc);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<job::MicroJobQueue> job = thread->GetCurrentEcmaContext()->GetMicroJobQueue();
JSHandle<TaggedArray> argv = factory->NewTaggedArray(5); // 5: 5 means parameters stored in array
argv->Set(thread, 0, promiseCapability->GetResolve());
argv->Set(thread, 1, promiseCapability->GetReject()); // 1 : reject method
argv->Set(thread, 2, dirPath); // 2 : current file path(containing file name)
argv->Set(thread, 3, specifier); // 3 : request module's path
argv->Set(thread, 4, recordName); // 4 : js recordName or undefined
JSHandle<JSFunction> dynamicImportJob(env->GetDynamicImportJob());
job::MicroJobQueue::EnqueueJob(thread, job, job::QueueType::QUEUE_PROMISE, dynamicImportJob, argv);
return promiseCapability->GetPromise();
}
JSTaggedValue RuntimeStubs::RuntimeEq(JSThread *thread, const JSHandle<JSTaggedValue> &left,
const JSHandle<JSTaggedValue> &right)
{
bool ret = JSTaggedValue::Equal(thread, left, right);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return (ret ? JSTaggedValue::True() : JSTaggedValue::False());
}
JSTaggedValue RuntimeStubs::RuntimeNotEq(JSThread *thread, const JSHandle<JSTaggedValue> &left,
const JSHandle<JSTaggedValue> &right)
{
bool ret = JSTaggedValue::Equal(thread, left, right);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return (ret ? JSTaggedValue::False() : JSTaggedValue::True());
}
JSTaggedValue RuntimeStubs::RuntimeLess(JSThread *thread, const JSHandle<JSTaggedValue> &left,
const JSHandle<JSTaggedValue> &right)
{
bool ret = JSTaggedValue::Compare(thread, left, right) == ComparisonResult::LESS;
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return (ret ? JSTaggedValue::True() : JSTaggedValue::False());
}
JSTaggedValue RuntimeStubs::RuntimeLessEq(JSThread *thread, const JSHandle<JSTaggedValue> &left,
const JSHandle<JSTaggedValue> &right)
{
bool ret = JSTaggedValue::Compare(thread, left, right) <= ComparisonResult::EQUAL;
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return (ret ? JSTaggedValue::True() : JSTaggedValue::False());
}
JSTaggedValue RuntimeStubs::RuntimeGreater(JSThread *thread, const JSHandle<JSTaggedValue> &left,
const JSHandle<JSTaggedValue> &right)
{
bool ret = JSTaggedValue::Compare(thread, left, right) == ComparisonResult::GREAT;
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return (ret ? JSTaggedValue::True() : JSTaggedValue::False());
}
JSTaggedValue RuntimeStubs::RuntimeGreaterEq(JSThread *thread, const JSHandle<JSTaggedValue> &left,
const JSHandle<JSTaggedValue> &right)
{
ComparisonResult comparison = JSTaggedValue::Compare(thread, left, right);
bool ret = (comparison == ComparisonResult::GREAT) || (comparison == ComparisonResult::EQUAL);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return (ret ? JSTaggedValue::True() : JSTaggedValue::False());
}
JSTaggedValue RuntimeStubs::RuntimeAdd2(JSThread *thread, const JSHandle<JSTaggedValue> &left,
const JSHandle<JSTaggedValue> &right)
{
if (left->IsString() && right->IsString()) {
EcmaString *resultStr = EcmaStringAccessor::Concat(
thread->GetEcmaVM(), JSHandle<EcmaString>(left), JSHandle<EcmaString>(right));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return JSTaggedValue(resultStr);
}
JSHandle<JSTaggedValue> primitiveA0(thread, JSTaggedValue::ToPrimitive(thread, left));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> primitiveA1(thread, JSTaggedValue::ToPrimitive(thread, right));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// contain string
if (primitiveA0->IsString() || primitiveA1->IsString()) {
JSHandle<EcmaString> stringA0 = JSTaggedValue::ToString(thread, primitiveA0);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<EcmaString> stringA1 = JSTaggedValue::ToString(thread, primitiveA1);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
EcmaString *resultStr = EcmaStringAccessor::Concat(thread->GetEcmaVM(), stringA0, stringA1);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return JSTaggedValue(resultStr);
}
JSHandle<JSTaggedValue> valLeft = JSTaggedValue::ToNumeric(thread, primitiveA0);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> valRight = JSTaggedValue::ToNumeric(thread, primitiveA1);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (valLeft->IsBigInt() || valRight->IsBigInt()) {
if (valLeft->IsBigInt() && valRight->IsBigInt()) {
JSHandle<BigInt> bigLeft(valLeft);
JSHandle<BigInt> bigRight(valRight);
return BigInt::Add(thread, bigLeft, bigRight).GetTaggedValue();
}
return RuntimeThrowTypeError(thread, "Cannot mix BigInt and other types, use explicit conversions");
}
double doubleA0 = valLeft->GetNumber();
double doubleA1 = valRight->GetNumber();
return JSTaggedValue(doubleA0 + doubleA1);
}
JSTaggedValue RuntimeStubs::RuntimeShl2(JSThread *thread,
const JSHandle<JSTaggedValue> &left,
const JSHandle<JSTaggedValue> &right)
{
JSHandle<JSTaggedValue> leftValue = JSTaggedValue::ToNumeric(thread, left);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> rightValue = JSTaggedValue::ToNumeric(thread, right);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (leftValue->IsBigInt() || rightValue->IsBigInt()) {
if (leftValue->IsBigInt() && rightValue->IsBigInt()) {
JSHandle<BigInt> leftBigint(leftValue);
JSHandle<BigInt> rightBigint(rightValue);
return BigInt::LeftShift(thread, leftBigint, rightBigint).GetTaggedValue();
}
return RuntimeThrowTypeError(thread, "Cannot mix BigInt and other types, use explicit conversions");
}
JSTaggedValue taggedNumber0 = RuntimeToJSTaggedValueWithInt32(thread, leftValue);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSTaggedValue taggedNumber1 = RuntimeToJSTaggedValueWithInt32(thread, rightValue);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
int32_t opNumber0 = taggedNumber0.GetInt();
int32_t opNumber1 = taggedNumber1.GetInt();
uint32_t shift =
static_cast<uint32_t>(opNumber1) & 0x1f; // NOLINT(hicpp-signed-bitwise, readability-magic-numbers)
using unsigned_type = std::make_unsigned_t<int32_t>;
auto ret =
static_cast<int32_t>(static_cast<unsigned_type>(opNumber0) << shift); // NOLINT(hicpp-signed-bitwise)
return JSTaggedValue(ret);
}
JSTaggedValue RuntimeStubs::RuntimeShr2(JSThread *thread, const JSHandle<JSTaggedValue> &left,
const JSHandle<JSTaggedValue> &right)
{
JSHandle<JSTaggedValue> valLeft = JSTaggedValue::ToNumeric(thread, left);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> valRight = JSTaggedValue::ToNumeric(thread, right);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (valLeft->IsBigInt() || valRight->IsBigInt()) {
if (valLeft->IsBigInt() && valRight->IsBigInt()) {
return BigInt::UnsignedRightShift(thread);
}
return RuntimeThrowTypeError(thread, "Cannot mix BigInt and other types, use explicit conversions");
}
JSTaggedValue taggedNumber0 = RuntimeToJSTaggedValueWithInt32(thread, valLeft);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSTaggedValue taggedNumber1 = RuntimeToJSTaggedValueWithInt32(thread, valRight);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
int32_t opNumber0 = taggedNumber0.GetInt();
int32_t opNumber1 = taggedNumber1.GetInt();
uint32_t shift = static_cast<uint32_t>(opNumber1) & 0x1f; // NOLINT(hicpp-signed-bitwise, readability-magic-numbers)
using unsigned_type = std::make_unsigned_t<uint32_t>;
auto ret =
static_cast<uint32_t>(static_cast<unsigned_type>(opNumber0) >> shift); // NOLINT(hicpp-signed-bitwise)
return JSTaggedValue(ret);
}
JSTaggedValue RuntimeStubs::RuntimeSub2(JSThread *thread, const JSHandle<JSTaggedValue> &left,
const JSHandle<JSTaggedValue> &right)
{
JSHandle<JSTaggedValue> valLeft = JSTaggedValue::ToNumeric(thread, left);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> valRight = JSTaggedValue::ToNumeric(thread, right);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (valLeft->IsBigInt() || valRight->IsBigInt()) {
if (valLeft->IsBigInt() && valRight->IsBigInt()) {
JSHandle<BigInt> bigLeft(valLeft);
JSHandle<BigInt> bigRight(valRight);
return BigInt::Subtract(thread, bigLeft, bigRight).GetTaggedValue();
}
return RuntimeThrowTypeError(thread, "Cannot mix BigInt and other types, use explicit conversions");
}
JSTaggedNumber number0(valLeft.GetTaggedValue());
JSTaggedNumber number1(valRight.GetTaggedValue());
return number0 - number1;
}
JSTaggedValue RuntimeStubs::RuntimeMul2(JSThread *thread, const JSHandle<JSTaggedValue> &left,
const JSHandle<JSTaggedValue> &right)
{
JSHandle<JSTaggedValue> valLeft = JSTaggedValue::ToNumeric(thread, left);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> valRight = JSTaggedValue::ToNumeric(thread, right);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 9. ReturnIfAbrupt(rnum).
if (valLeft->IsBigInt() || valRight->IsBigInt()) {
if (valLeft->IsBigInt() && valRight->IsBigInt()) {
JSHandle<BigInt> bigLeft(valLeft);
JSHandle<BigInt> bigRight(valRight);
return BigInt::Multiply(thread, bigLeft, bigRight).GetTaggedValue();
}
return RuntimeThrowTypeError(thread, "Cannot mix BigInt and other types, use explicit conversions");
}
// 12.6.3.1 Applying the * Operator
JSTaggedNumber number0(valLeft.GetTaggedValue());
JSTaggedNumber number1(valRight.GetTaggedValue());
return number0 * number1;
}
JSTaggedValue RuntimeStubs::RuntimeDiv2(JSThread *thread, const JSHandle<JSTaggedValue> &left,
const JSHandle<JSTaggedValue> &right)
{
JSHandle<JSTaggedValue> valLeft = JSTaggedValue::ToNumeric(thread, left);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> valRight = JSTaggedValue::ToNumeric(thread, right);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (valLeft->IsBigInt() || valRight->IsBigInt()) {
if (valLeft->IsBigInt() && valRight->IsBigInt()) {
JSHandle<BigInt> bigLeft(valLeft);
JSHandle<BigInt> bigRight(valRight);
return BigInt::Divide(thread, bigLeft, bigRight).GetTaggedValue();
}
return RuntimeThrowTypeError(thread, "Cannot mix BigInt and other types, use explicit conversions");
}
double dLeft = valLeft->GetNumber();
double dRight = valRight->GetNumber();
if (dRight == 0) {
if (dLeft == 0 || std::isnan(dLeft)) {
return JSTaggedValue(base::NAN_VALUE);
}
bool positive = (((base::bit_cast<uint64_t>(dRight)) & base::DOUBLE_SIGN_MASK) ==
((base::bit_cast<uint64_t>(dLeft)) & base::DOUBLE_SIGN_MASK));
return JSTaggedValue(positive ? base::POSITIVE_INFINITY : -base::POSITIVE_INFINITY);
}
return JSTaggedValue(dLeft / dRight);
}
JSTaggedValue RuntimeStubs::RuntimeMod2(JSThread *thread, const JSHandle<JSTaggedValue> &left,
const JSHandle<JSTaggedValue> &right)
{
JSHandle<JSTaggedValue> valLeft = JSTaggedValue::ToNumeric(thread, left);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> valRight = JSTaggedValue::ToNumeric(thread, right);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 12.6.3.3 Applying the % Operator
if (valLeft->IsBigInt() || valRight->IsBigInt()) {
if (valLeft->IsBigInt() && valRight->IsBigInt()) {
JSHandle<BigInt> leftBigint(valLeft);
JSHandle<BigInt> rightBigint(valRight);
return BigInt::Remainder(thread, leftBigint, rightBigint).GetTaggedValue();
}
return RuntimeThrowTypeError(thread, "Cannot mix BigInt and other types, use explicit conversions");
}
double dLeft = valLeft->GetNumber();
double dRight = valRight->GetNumber();
// 12.6.3.3 Applying the % Operator
if ((dRight == 0.0) || std::isnan(dRight) || std::isnan(dLeft) || std::isinf(dLeft)) {
return JSTaggedValue(base::NAN_VALUE);
}
if ((dLeft == 0.0) || std::isinf(dRight)) {
return JSTaggedValue(dLeft);
}
return JSTaggedValue(std::fmod(dLeft, dRight));
}
JSTaggedValue RuntimeStubs::RuntimeAshr2(JSThread *thread, const JSHandle<JSTaggedValue> &left,
const JSHandle<JSTaggedValue> &right)
{
JSHandle<JSTaggedValue> valLeft = JSTaggedValue::ToNumeric(thread, left);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> valRight = JSTaggedValue::ToNumeric(thread, right);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (valLeft->IsBigInt() || valRight->IsBigInt()) {
if (valLeft->IsBigInt() && valRight->IsBigInt()) {
JSHandle<BigInt> bigLeft(valLeft);
JSHandle<BigInt> bigRight(valRight);
return BigInt::SignedRightShift(thread, bigLeft, bigRight).GetTaggedValue();
}
return RuntimeThrowTypeError(thread, "Cannot mix BigInt and other types, use explicit conversions");
}
JSTaggedValue taggedNumber0 = RuntimeToJSTaggedValueWithInt32(thread, valLeft);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSTaggedValue taggedNumber1 = RuntimeToJSTaggedValueWithInt32(thread, valRight);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
int32_t opNumber0 = taggedNumber0.GetInt();
int32_t opNumber1 = taggedNumber1.GetInt();
uint32_t shift = static_cast<uint32_t>(opNumber1) & 0x1f; // NOLINT(hicpp-signed-bitwise, readability-magic-numbers)
auto ret = static_cast<int32_t>(opNumber0 >> shift); // NOLINT(hicpp-signed-bitwise)
return JSTaggedValue(ret);
}
JSTaggedValue RuntimeStubs::RuntimeAnd2(JSThread *thread, const JSHandle<JSTaggedValue> &left,
const JSHandle<JSTaggedValue> &right)
{
JSHandle<JSTaggedValue> valLeft = JSTaggedValue::ToNumeric(thread, left);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> valRight = JSTaggedValue::ToNumeric(thread, right);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (valLeft->IsBigInt() || valRight->IsBigInt()) {
if (valLeft->IsBigInt() && valRight->IsBigInt()) {
JSHandle<BigInt> leftBigint(valLeft);
JSHandle<BigInt> rightBigint(valRight);
return BigInt::BitwiseAND(thread, leftBigint, rightBigint).GetTaggedValue();
}
return RuntimeThrowTypeError(thread, "Cannot mix BigInt and other types, use explicit conversions");
}
JSTaggedValue taggedNumber0 = RuntimeToJSTaggedValueWithInt32(thread, valLeft);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSTaggedValue taggedNumber1 = RuntimeToJSTaggedValueWithInt32(thread, valRight);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
int32_t opNumber0 = taggedNumber0.GetInt();
int32_t opNumber1 = taggedNumber1.GetInt();
// NOLINT(hicpp-signed-bitwise)
auto ret = static_cast<uint32_t>(opNumber0) & static_cast<uint32_t>(opNumber1);
return JSTaggedValue(static_cast<int32_t>(ret));
}
JSTaggedValue RuntimeStubs::RuntimeOr2(JSThread *thread, const JSHandle<JSTaggedValue> &left,
const JSHandle<JSTaggedValue> &right)
{
JSHandle<JSTaggedValue> valLeft = JSTaggedValue::ToNumeric(thread, left);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> valRight = JSTaggedValue::ToNumeric(thread, right);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (valLeft->IsBigInt() || valRight->IsBigInt()) {
if (valLeft->IsBigInt() && valRight->IsBigInt()) {
JSHandle<BigInt> leftBigint(valLeft);
JSHandle<BigInt> rightBigint(valRight);
return BigInt::BitwiseOR(thread, leftBigint, rightBigint).GetTaggedValue();
}
return RuntimeThrowTypeError(thread, "Cannot mix BigInt and other types, use explicit conversions");
}
JSTaggedValue taggedNumber0 = RuntimeToJSTaggedValueWithInt32(thread, valLeft);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSTaggedValue taggedNumber1 = RuntimeToJSTaggedValueWithInt32(thread, valRight);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
int32_t opNumber0 = taggedNumber0.GetInt();
int32_t opNumber1 = taggedNumber1.GetInt();
// NOLINT(hicpp-signed-bitwise)
auto ret = static_cast<uint32_t>(opNumber0) | static_cast<uint32_t>(opNumber1);
return JSTaggedValue(static_cast<int32_t>(ret));
}
JSTaggedValue RuntimeStubs::RuntimeXor2(JSThread *thread, const JSHandle<JSTaggedValue> &left,
const JSHandle<JSTaggedValue> &right)
{
JSHandle<JSTaggedValue> valLeft = JSTaggedValue::ToNumeric(thread, left);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> valRight = JSTaggedValue::ToNumeric(thread, right);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (valLeft->IsBigInt() || valRight->IsBigInt()) {
if (valLeft->IsBigInt() && valRight->IsBigInt()) {
JSHandle<BigInt> leftBigint(valLeft);
JSHandle<BigInt> rightBigint(valRight);
return BigInt::BitwiseXOR(thread, leftBigint, rightBigint).GetTaggedValue();
}
return RuntimeThrowTypeError(thread, "Cannot mix BigInt and other types, use explicit conversions");
}
JSTaggedValue taggedNumber0 = RuntimeToJSTaggedValueWithInt32(thread, valLeft);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSTaggedValue taggedNumber1 = RuntimeToJSTaggedValueWithInt32(thread, valRight);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
int32_t opNumber0 = taggedNumber0.GetInt();
int32_t opNumber1 = taggedNumber1.GetInt();
// NOLINT(hicpp-signed-bitwise)
auto ret = static_cast<uint32_t>(opNumber0) ^ static_cast<uint32_t>(opNumber1);
return JSTaggedValue(static_cast<int32_t>(ret));
}
JSTaggedValue RuntimeStubs::RuntimeToJSTaggedValueWithInt32(JSThread *thread,
const JSHandle<JSTaggedValue> &value)
{
int32_t res = JSTaggedValue::ToInt32(thread, value);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return JSTaggedValue(res);
}
JSTaggedValue RuntimeStubs::RuntimeToJSTaggedValueWithUint32(JSThread *thread, const JSHandle<JSTaggedValue> &value)
{
uint32_t res = JSTaggedValue::ToUint32(thread, value);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return JSTaggedValue(res);
}
JSTaggedValue RuntimeStubs::RuntimeCreateEmptyObject([[maybe_unused]] JSThread *thread, ObjectFactory *factory,
JSHandle<GlobalEnv> globalEnv)
{
JSHandle<JSFunction> builtinObj(globalEnv->GetObjectFunction());
JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(builtinObj);
return obj.GetTaggedValue();
}
JSTaggedValue RuntimeStubs::RuntimeCreateEmptyArray([[maybe_unused]] JSThread *thread, ObjectFactory *factory,
JSHandle<GlobalEnv> globalEnv)
{
JSHandle<JSFunction> builtinObj(globalEnv->GetArrayFunction());
JSHandle<JSObject> arr = factory->NewJSObjectByConstructor(builtinObj);
return arr.GetTaggedValue();
}
JSTaggedValue RuntimeStubs::RuntimeGetUnmapedArgs(JSThread *thread, JSTaggedType *sp, uint32_t actualNumArgs,
uint32_t startIdx)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<TaggedArray> argumentsList = factory->NewTaggedArray(actualNumArgs);
for (uint32_t i = 0; i < actualNumArgs; ++i) {
argumentsList->Set(thread, i,
JSTaggedValue(sp[startIdx + i])); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
}
return RuntimeGetUnmapedJSArgumentObj(thread, argumentsList);
}
JSTaggedValue RuntimeStubs::RuntimeCopyRestArgs(JSThread *thread, JSTaggedType *sp, uint32_t restNumArgs,
uint32_t startIdx)
{
return JSArray::ArrayCreateWithInit(
thread, restNumArgs, [thread, sp, startIdx] (const JSHandle<TaggedArray> &newElements, uint32_t length) {
for (uint32_t i = 0; i < length; ++i) {
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
newElements->Set(thread, i, JSTaggedValue(sp[startIdx + i]));
}
});
}
JSTaggedValue RuntimeStubs::RuntimeCreateArrayWithBuffer(JSThread *thread, ObjectFactory *factory,
const JSHandle<JSTaggedValue> &literal)
{
JSHandle<JSArray> array(literal);
JSHandle<JSArray> arrLiteral = factory->CloneArrayLiteral(array);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return arrLiteral.GetTaggedValue();
}
JSTaggedValue RuntimeStubs::RuntimeCreateObjectWithBuffer(JSThread *thread, ObjectFactory *factory,
const JSHandle<JSObject> &literal)
{
JSHandle<JSObject> objLiteral = factory->CloneObjectLiteral(literal);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return objLiteral.GetTaggedValue();
}
JSTaggedValue RuntimeStubs::RuntimeNewLexicalEnv(JSThread *thread, uint16_t numVars)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<LexicalEnv> newEnv = factory->NewLexicalEnv(numVars);
JSTaggedValue currentLexenv = thread->GetCurrentLexenv();
newEnv->SetParentEnv(thread, currentLexenv);
newEnv->SetScopeInfo(thread, JSTaggedValue::Hole());
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return newEnv.GetTaggedValue();
}
JSTaggedValue RuntimeStubs::RuntimeNewSendableEnv(JSThread *thread, uint16_t numVars)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<SendableEnv> newEnv = factory->NewSendableEnv(numVars);
JSTaggedValue module = JSFunction::Cast(thread->GetCurrentFunction())->GetModule();
JSHandle<SourceTextModule> moduleHandle(thread, module);
newEnv->SetParentEnv(thread, moduleHandle->GetSendableEnv());
newEnv->SetScopeInfo(thread, JSTaggedValue::Hole());
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return newEnv.GetTaggedValue();
}
JSTaggedValue RuntimeStubs::RuntimeNewObjRange(JSThread *thread, const JSHandle<JSTaggedValue> &func,
const JSHandle<JSTaggedValue> &newTarget, uint16_t firstArgIdx, uint16_t length)
{
JSHandle<JSTaggedValue> preArgs(thread, JSTaggedValue::Undefined());
JSHandle<TaggedArray> args = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(length);
FrameHandler frameHandler(thread);
for (uint16_t i = 0; i < length; ++i) {
JSTaggedValue value = frameHandler.GetVRegValue(firstArgIdx + i);
args->Set(thread, i, value);
}
auto tagged = RuntimeOptConstruct(thread, func, newTarget, preArgs, args);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return tagged;
}
void RuntimeStubs::DefineFuncTryUseAOTHClass(JSThread *thread, const JSHandle<JSFunction> &func,
const JSHandle<JSTaggedValue> &ihc)
{
FunctionKind kind = Method::Cast(func->GetMethod())->GetFunctionKind();
// The HClass of AOT comes from .ai deserialization
if (!ihc->IsUndefined() && kind == FunctionKind::BASE_CONSTRUCTOR) {
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
func->SetProtoOrHClass(thread, ihc);
// build inheritance
JSHandle<JSTaggedValue> parentPrototype = env->GetObjectFunctionPrototype();
JSHandle<JSObject> clsPrototype(thread, func->GetFunctionPrototype());
clsPrototype->GetClass()->SetPrototype(thread, parentPrototype);
// set "constructor" in prototype
JSHandle<JSTaggedValue> constructorKey = globalConst->GetHandledConstructorString();
PropertyDescriptor descriptor(thread, JSHandle<JSTaggedValue>::Cast(func), true, false, true);
JSObject::DefineOwnProperty(thread, clsPrototype, constructorKey, descriptor);
// enable prototype change marker
JSHClass::EnablePHCProtoChangeMarker(thread,
JSHandle<JSHClass>(thread, parentPrototype->GetTaggedObject()->GetClass()));
if (ihc->IsJSHClass()) {
JSHClass::EnableProtoChangeMarker(thread, JSHandle<JSHClass>(ihc));
} else {
JSHClass::EnablePHCProtoChangeMarker(thread, JSHandle<JSHClass>(thread, clsPrototype->GetClass()));
}
}
}
JSTaggedValue RuntimeStubs::RuntimeDefinefunc(JSThread *thread, const JSHandle<JSTaggedValue> &constpool,
uint16_t methodId, const JSHandle<JSTaggedValue> &module,
uint16_t length, const JSHandle<JSTaggedValue> &envHandle,
const JSHandle<JSTaggedValue> &homeObject)
{
JSHandle<ConstantPool> constpoolHandle = JSHandle<ConstantPool>::Cast(constpool);
JSMutableHandle<JSTaggedValue> ihc(thread, JSTaggedValue::Undefined());
JSTaggedValue val = constpoolHandle->GetObjectFromCache(methodId);
if (val.IsAOTLiteralInfo()) {
JSTaggedValue unsharedCp =
thread->GetCurrentEcmaContext()->FindOrCreateUnsharedConstpool(constpool.GetTaggedValue());
JSHandle<ConstantPool> unsharedCpHandle(thread, unsharedCp);
val = unsharedCpHandle->GetObjectFromCache(methodId);
JSHandle<AOTLiteralInfo> aotLiteralInfo(thread, val);
ihc.Update(aotLiteralInfo->GetIhc());
}
JSTaggedValue method = ConstantPool::GetMethodFromCache(thread, constpool.GetTaggedValue(), methodId);
const JSHandle<Method> methodHandle(thread, method);
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<JSFunction> result = factory->NewJSFunction(methodHandle);
DefineFuncTryUseAOTHClass(thread, result, ihc);
result->SetLength(length);
result->SetLexicalEnv(thread, envHandle.GetTaggedValue());
result->SetHomeObject(thread, homeObject.GetTaggedValue());
result->SetModule(thread, module.GetTaggedValue());
QuickFixHelper::SetPatchModule(thread, methodHandle, result);
return result.GetTaggedValue();
}
JSTaggedValue RuntimeStubs::RuntimeCreateRegExpWithLiteral(JSThread *thread,
const JSHandle<JSTaggedValue> &pattern, uint8_t flags)
{
JSHandle<JSTaggedValue> flagsHandle(thread, JSTaggedValue(flags));
return builtins::BuiltinsRegExp::RegExpCreate(thread, pattern, flagsHandle);
}
JSTaggedValue RuntimeStubs::RuntimeThrowIfSuperNotCorrectCall(JSThread *thread, uint16_t index,
JSTaggedValue thisValue)
{
if (index == 0 && (thisValue.IsUndefined() || thisValue.IsHole())) {
return RuntimeThrowReferenceError(thread, JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
"sub-class must call super before use 'this'");
}
if (index == 1 && !thisValue.IsUndefined() && !thisValue.IsHole()) {
return RuntimeThrowReferenceError(thread, JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
"super() forbidden re-bind 'this'");
}
return JSTaggedValue::True();
}
JSTaggedValue RuntimeStubs::RuntimeCreateObjectHavingMethod(JSThread *thread, ObjectFactory *factory,
const JSHandle<JSObject> &literal,
const JSHandle<JSTaggedValue> &env)
{
JSHandle<JSObject> objLiteral = factory->CloneObjectLiteral(literal, env);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return objLiteral.GetTaggedValue();
}
JSTaggedValue RuntimeStubs::CommonCreateObjectWithExcludedKeys(JSThread *thread,
const JSHandle<JSTaggedValue> &objVal,
uint32_t numExcludedKeys,
JSHandle<TaggedArray> excludedKeys)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<JSObject> restObj = factory->NewEmptyJSObject();
if (objVal->IsNull() || objVal->IsUndefined()) {
return restObj.GetTaggedValue();
}
JSHandle<JSObject> obj(JSTaggedValue::ToObject(thread, objVal));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<TaggedArray> allKeys = JSObject::GetOwnPropertyKeys(thread, obj);
uint32_t numAllKeys = allKeys->GetLength();
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
for (uint32_t i = 0; i < numAllKeys; i++) {
key.Update(allKeys->Get(i));
bool isExcludedKey = false;
for (uint32_t j = 0; j < numExcludedKeys; j++) {
if (JSTaggedValue::Equal(thread, key, JSHandle<JSTaggedValue>(thread, excludedKeys->Get(j)))) {
isExcludedKey = true;
break;
}
}
if (!isExcludedKey) {
PropertyDescriptor desc(thread);
bool success = JSObject::GetOwnProperty(thread, obj, key, desc);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (success && desc.IsEnumerable()) {
JSHandle<JSTaggedValue> value = JSObject::GetProperty(thread, obj, key).GetValue();
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSObject::SetProperty(thread, restObj, key, value, true);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
}
}
return restObj.GetTaggedValue();
}
JSTaggedValue RuntimeStubs::RuntimeOptCreateObjectWithExcludedKeys(JSThread *thread, uintptr_t argv, uint32_t argc)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
const JSHandle<JSTaggedValue> &objVal = GetHArg<JSTaggedValue>(argv, argc, 0); // 0: means the objVal
uint32_t firstArgRegIdx = 1; // 1: means the firstArgRegIdx
uint32_t numExcludedKeys = 0;
uint32_t numKeys = argc - 1;
JSHandle<TaggedArray> excludedKeys = factory->NewTaggedArray(numKeys);
JSTaggedValue excludedKey = GetArg(argv, argc, firstArgRegIdx);
if (!excludedKey.IsUndefined()) {
numExcludedKeys = numKeys;
excludedKeys->Set(thread, 0, excludedKey);
for (uint32_t i = 1; i < numExcludedKeys; i++) {
excludedKey = GetArg(argv, argc, firstArgRegIdx + i);
excludedKeys->Set(thread, i, excludedKey);
}
}
return CommonCreateObjectWithExcludedKeys(thread, objVal, numExcludedKeys, excludedKeys);
}
JSTaggedValue RuntimeStubs::RuntimeCreateObjectWithExcludedKeys(JSThread *thread, uint16_t numKeys,
const JSHandle<JSTaggedValue> &objVal,
uint16_t firstArgRegIdx)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
uint32_t numExcludedKeys = 0;
JSHandle<TaggedArray> excludedKeys = factory->NewTaggedArray(numKeys + 1);
FrameHandler frameHandler(thread);
JSTaggedValue excludedKey = frameHandler.GetVRegValue(firstArgRegIdx);
if (!excludedKey.IsUndefined()) {
numExcludedKeys = numKeys + 1;
excludedKeys->Set(thread, 0, excludedKey);
for (uint32_t i = 1; i < numExcludedKeys; i++) {
excludedKey = frameHandler.GetVRegValue(firstArgRegIdx + i);
excludedKeys->Set(thread, i, excludedKey);
}
}
JSHandle<JSTaggedValue> finalVal = objVal;
if (finalVal->CheckIsJSProxy()) {
JSHandle<JSProxy> proxyVal(thread, finalVal.GetTaggedValue());
finalVal = proxyVal->GetSourceTarget(thread);
}
return CommonCreateObjectWithExcludedKeys(thread, finalVal, numExcludedKeys, excludedKeys);
}
JSTaggedValue RuntimeStubs::RuntimeDefineMethod(JSThread *thread, const JSHandle<Method> &methodHandle,
const JSHandle<JSTaggedValue> &homeObject, uint16_t length,
const JSHandle<JSTaggedValue> &env,
const JSHandle<JSTaggedValue> &module)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<JSFunction> func = factory->NewJSFunction(methodHandle, homeObject);
func->SetLength(length);
func->SetLexicalEnv(thread, env);
func->SetModule(thread, module);
QuickFixHelper::SetPatchModule(thread, methodHandle, func);
return func.GetTaggedValue();
}
JSTaggedValue RuntimeStubs::RuntimeLdSendableClass(const JSHandle<JSTaggedValue> &env, uint16_t level)
{
JSTaggedValue currentEnv(env.GetTaggedValue());
for (uint32_t i = 0; i < level; i++) {
ASSERT(currentEnv.IsLexicalEnv());
currentEnv = LexicalEnv::Cast(currentEnv.GetTaggedObject())->GetParentEnv();
}
ASSERT(currentEnv.IsJSSharedFunction());
return currentEnv;
}
JSTaggedValue RuntimeStubs::RuntimeCallSpread(JSThread *thread,
const JSHandle<JSTaggedValue> &func,
const JSHandle<JSTaggedValue> &obj,
const JSHandle<JSTaggedValue> &array)
{
if ((!obj->IsUndefined() && !obj->IsECMAObject()) || !func->IsCallable() || !array->IsJSArray()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "cannot Callspread", JSTaggedValue::Exception());
}
JSHandle<TaggedArray> coretypesArray(thread, RuntimeGetCallSpreadArgs(thread, array));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
uint32_t length = coretypesArray->GetLength();
JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, obj, undefined, length);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(length, coretypesArray);
return EcmaInterpreter::Execute(info);
}
void RuntimeStubs::RuntimeSetPatchModule(JSThread *thread, const JSHandle<JSFunction> &func)
{
JSHandle<Method> methodHandle(thread, Method::Cast(func->GetMethod()));
const JSHandle<JSTaggedValue> coldReloadRecordName =
thread->GetCurrentEcmaContext()->FindPatchModule(MethodLiteral::GetRecordName(
methodHandle->GetJSPandaFile(), methodHandle->GetMethodId()));
if (!coldReloadRecordName->IsHole()) {
func->SetModule(thread, coldReloadRecordName.GetTaggedValue());
}
}
JSTaggedValue RuntimeStubs::RuntimeDefineGetterSetterByValue(JSThread *thread, const JSHandle<JSObject> &obj,
const JSHandle<JSTaggedValue> &prop,
const JSHandle<JSTaggedValue> &getter,
const JSHandle<JSTaggedValue> &setter, bool flag,
const JSHandle<JSTaggedValue> &func,
int32_t pcOffset)
{
JSHandle<JSTaggedValue> propKey = JSTaggedValue::ToPropertyKey(thread, prop);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
auto globalConst = thread->GlobalConstants();
if (obj.GetTaggedValue().IsClassConstructor() &&
JSTaggedValue::SameValue(propKey, globalConst->GetHandledPrototypeString())) {
return RuntimeThrowTypeError(
thread,
"In a class, computed property names for static getter that are named 'prototype' throw a TypeError");
}
auto receiverHClass = obj->GetJSHClass();
if (flag) {
if (!getter->IsUndefined()) {
if (propKey->IsNumber()) {
propKey =
JSHandle<JSTaggedValue>::Cast(base::NumberHelper::NumberToString(thread, propKey.GetTaggedValue()));
}
JSFunctionBase::SetFunctionName(thread, JSHandle<JSFunctionBase>::Cast(getter), propKey,
JSHandle<JSTaggedValue>(thread, globalConst->GetGetString()));
}
if (!setter->IsUndefined()) {
if (propKey->IsNumber()) {
propKey =
JSHandle<JSTaggedValue>::Cast(base::NumberHelper::NumberToString(thread, propKey.GetTaggedValue()));
}
JSFunctionBase::SetFunctionName(thread, JSHandle<JSFunctionBase>::Cast(setter), propKey,
JSHandle<JSTaggedValue>(thread, globalConst->GetSetString()));
}
}
// set accessor
bool enumerable =
!(obj.GetTaggedValue().IsClassPrototype() || obj.GetTaggedValue().IsClassConstructor());
PropertyDescriptor desc(thread, true, enumerable, true);
if (!getter->IsUndefined()) {
Method *method = Method::Cast(JSHandle<JSFunction>::Cast(getter)->GetMethod().GetTaggedObject());
method->SetFunctionKind(FunctionKind::GETTER_FUNCTION);
desc.SetGetter(getter);
}
if (!setter->IsUndefined()) {
Method *method = Method::Cast(JSHandle<JSFunction>::Cast(setter)->GetMethod().GetTaggedObject());
method->SetFunctionKind(FunctionKind::SETTER_FUNCTION);
desc.SetSetter(setter);
}
JSObject::DefineOwnProperty(thread, obj, propKey, desc);
auto holderTraHClass = obj->GetJSHClass();
if (receiverHClass != holderTraHClass) {
if (holderTraHClass->IsTS()) {
JSHandle<JSHClass> phcHandle(thread, holderTraHClass);
JSHClass::EnablePHCProtoChangeMarker(thread, phcHandle);
}
if (thread->GetEcmaVM()->IsEnablePGOProfiler()) {
if (!func->IsUndefined()) {
thread->GetEcmaVM()->GetPGOProfiler()->ProfileDefineGetterSetter(
receiverHClass, holderTraHClass, func, pcOffset);
}
}
}
return obj.GetTaggedValue();
}
JSTaggedValue RuntimeStubs::RuntimeSuperCall(JSThread *thread, const JSHandle<JSTaggedValue> &func,
const JSHandle<JSTaggedValue> &newTarget, uint16_t firstVRegIdx,
uint16_t length)
{
JSHandle<JSTaggedValue> superFunc(thread, JSTaggedValue::GetPrototype(thread, func));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<TaggedArray> argv = factory->NewTaggedArray(length);
FrameHandler frameHandler(thread);
for (size_t i = 0; i < length; ++i) {
argv->Set(thread, i, frameHandler.GetVRegValue(firstVRegIdx + i));
}
JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, superFunc, undefined, newTarget, length);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(length, argv);
JSTaggedValue result = JSFunction::Construct(info);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return result;
}
JSTaggedValue RuntimeStubs::RuntimeOptSuperCall(JSThread *thread, uintptr_t argv, uint32_t argc)
{
constexpr size_t fixNums = 2;
JSHandle<JSTaggedValue> func = GetHArg<JSTaggedValue>(argv, argc, 0);
JSHandle<JSTaggedValue> newTarget = GetHArg<JSTaggedValue>(argv, argc, 1);
JSHandle<JSTaggedValue> superFunc(thread, JSTaggedValue::GetPrototype(thread, func));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
uint16_t length = argc - fixNums;
JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, superFunc, undefined, newTarget, length);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
for (size_t i = 0; i < length; ++i) {
JSTaggedType arg = reinterpret_cast<JSTaggedType *>(argv)[i + fixNums];
info->SetCallArg(i, JSTaggedValue(arg));
}
JSTaggedValue result = JSFunction::Construct(info);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return result;
}
JSTaggedValue RuntimeStubs::RuntimeThrowTypeError(JSThread *thread, const char *message)
{
ASSERT_NO_ABRUPT_COMPLETION(thread);
THROW_TYPE_ERROR_AND_RETURN(thread, message, JSTaggedValue::Exception());
}
JSTaggedValue RuntimeStubs::RuntimeGetCallSpreadArgs(JSThread *thread, const JSHandle<JSTaggedValue> &jsArray)
{
uint32_t argvMayMaxLength = JSHandle<JSArray>::Cast(jsArray)->GetArrayLength();
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<TaggedArray> argv = factory->NewTaggedArray(argvMayMaxLength);
JSHandle<JSTaggedValue> itor = JSIterator::GetIterator(thread, jsArray);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// Fast path when array is stablearray and Iterator not change.
if (jsArray->IsStableJSArray(thread) && itor->IsJSArrayIterator()) {
JSHandle<JSObject> jsArrayObj(jsArray);
ElementAccessor::CopyJSArrayToTaggedArray(thread, jsArrayObj, argv, argvMayMaxLength);
return argv.GetTaggedValue();
}
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSMutableHandle<JSTaggedValue> next(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> nextArg(thread, JSTaggedValue::Undefined());
size_t argvIndex = 0;
while (true) {
next.Update(JSIterator::IteratorStep(thread, itor).GetTaggedValue());
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (JSTaggedValue::SameValue(next.GetTaggedValue(), JSTaggedValue::False())) {
break;
}
nextArg.Update(JSIterator::IteratorValue(thread, next).GetTaggedValue());
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (UNLIKELY(argvIndex + 1 >= argvMayMaxLength)) {
argvMayMaxLength = argvMayMaxLength + (argvMayMaxLength >> 1U) + 1U;
argv = argv->SetCapacity(thread, argv, argvMayMaxLength);
}
argv->Set(thread, argvIndex++, nextArg);
}
argv = factory->CopyArray(argv, argvMayMaxLength, argvIndex);
return argv.GetTaggedValue();
}
JSTaggedValue RuntimeStubs::RuntimeThrowSyntaxError(JSThread *thread, const char *message)
{
ASSERT_NO_ABRUPT_COMPLETION(thread);
THROW_SYNTAX_ERROR_AND_RETURN(thread, message, JSTaggedValue::Exception());
}
JSTaggedValue RuntimeStubs::RuntimeLdBigInt(JSThread *thread, const JSHandle<JSTaggedValue> &numberBigInt)
{
return JSTaggedValue::ToBigInt(thread, numberBigInt);
}
JSTaggedValue RuntimeStubs::RuntimeCallBigIntAsIntN(JSThread *thread, JSTaggedValue bits, JSTaggedValue bigint)
{
auto biginteger = JSHandle<BigInt>(thread, bigint);
JSTaggedNumber bitness = JSTaggedValue::ToNumber(thread, bits);
return BigInt::AsintN(thread, bitness, biginteger);
}
JSTaggedValue RuntimeStubs::RuntimeCallBigIntAsUintN(JSThread *thread, JSTaggedValue bits, JSTaggedValue bigint)
{
auto biginteger = JSHandle<BigInt>(thread, bigint);
JSTaggedNumber bitness = JSTaggedValue::ToNumber(thread, bits);
return BigInt::AsUintN(thread, bitness, biginteger);
}
JSTaggedValue RuntimeStubs::RuntimeNewLexicalEnvWithName(JSThread *thread, uint16_t numVars, uint16_t scopeId)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<LexicalEnv> newEnv = factory->NewLexicalEnv(numVars);
JSTaggedValue currentLexenv = thread->GetCurrentLexenv();
newEnv->SetParentEnv(thread, currentLexenv);
JSTaggedValue scopeInfo = ScopeInfoExtractor::GenerateScopeInfo(thread, scopeId);
newEnv->SetScopeInfo(thread, scopeInfo);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return newEnv.GetTaggedValue();
}
JSTaggedValue RuntimeStubs::RuntimeOptGetUnmapedArgs(JSThread *thread, uint32_t actualNumArgs)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<TaggedArray> argumentsList = factory->NewTaggedArray(actualNumArgs - NUM_MANDATORY_JSFUNC_ARGS);
auto argv = GetActualArgvFromStub(thread);
int idx = 0;
for (uint32_t i = NUM_MANDATORY_JSFUNC_ARGS; i < actualNumArgs; ++i) {
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
JSTaggedType arg = reinterpret_cast<JSTaggedType *>(argv)[i];
JSTaggedValue args = JSTaggedValue(arg);
argumentsList->Set(thread, idx++, args);
}
return RuntimeGetUnmapedJSArgumentObj(thread, argumentsList);
}
JSTaggedValue RuntimeStubs::RuntimeGetUnmapedJSArgumentObj(JSThread *thread, const JSHandle<TaggedArray> &argumentsList)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<GlobalEnv> globalEnv = thread->GetEcmaVM()->GetGlobalEnv();
// 1. Let len be the number of elements in argumentsList
uint32_t len = argumentsList->GetLength();
// 2. Let obj be ObjectCreate(%ObjectPrototype%, «[[ParameterMap]]»).
// 3. Set objs [[ParameterMap]] internal slot to undefined.
// [[ParameterMap]] setted as undifined.
JSHandle<JSArguments> obj = factory->NewJSArguments();
// 4. Perform DefinePropertyOrThrow(obj, "length", PropertyDescriptor{[[Value]]: len, [[Writable]]: true,
// [[Enumerable]]: false, [[Configurable]]: true}).
obj->SetPropertyInlinedProps(thread, JSArguments::LENGTH_INLINE_PROPERTY_INDEX, JSTaggedValue(len));
// 5. Let index be 0.
// 6. Repeat while index < len,
// a. Let val be argumentsList[index].
// b. Perform CreateDataProperty(obj, ToString(index), val).
// c. Let index be index + 1
obj->SetElements(thread, argumentsList.GetTaggedValue());
// 7. Perform DefinePropertyOrThrow(obj, @@iterator, PropertyDescriptor
// {[[Value]]:%ArrayProto_values%,
// [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}).
obj->SetPropertyInlinedProps(thread, JSArguments::ITERATOR_INLINE_PROPERTY_INDEX,
globalEnv->GetArrayProtoValuesFunction().GetTaggedValue());
// 8. Perform DefinePropertyOrThrow(obj, "caller", PropertyDescriptor {[[Get]]: %ThrowTypeError%,
// [[Set]]: %ThrowTypeError%, [[Enumerable]]: false, [[Configurable]]: false}).
JSHandle<JSTaggedValue> accessorCaller = globalEnv->GetArgumentsCallerAccessor();
obj->SetPropertyInlinedProps(thread, JSArguments::CALLER_INLINE_PROPERTY_INDEX, accessorCaller.GetTaggedValue());
// 9. Perform DefinePropertyOrThrow(obj, "callee", PropertyDescriptor {[[Get]]: %ThrowTypeError%,
// [[Set]]: %ThrowTypeError%, [[Enumerable]]: false, [[Configurable]]: false}).
JSHandle<JSTaggedValue> accessorCallee = globalEnv->GetArgumentsCalleeAccessor();
obj->SetPropertyInlinedProps(thread, JSArguments::CALLEE_INLINE_PROPERTY_INDEX, accessorCallee.GetTaggedValue());
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 11. Return obj
return obj.GetTaggedValue();
}
JSTaggedValue RuntimeStubs::RuntimeOptNewLexicalEnvWithName(JSThread *thread, uint16_t numVars, uint16_t scopeId,
JSHandle<JSTaggedValue> &currentLexEnv,
JSHandle<JSTaggedValue> &func)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<LexicalEnv> newEnv = factory->NewLexicalEnv(numVars);
newEnv->SetParentEnv(thread, currentLexEnv.GetTaggedValue());
JSTaggedValue scopeInfo = RuntimeOptGenerateScopeInfo(thread, scopeId, func.GetTaggedValue());
newEnv->SetScopeInfo(thread, scopeInfo);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSTaggedValue taggedEnv = newEnv.GetTaggedValue();
return taggedEnv;
}
JSTaggedValue RuntimeStubs::RuntimeOptCopyRestArgs(JSThread *thread, uint32_t actualArgc, uint32_t restIndex)
{
// when only have three fixed args, restIndex in bytecode maybe not zero, but it actually should be zero.
uint32_t actualRestNum = 0;
if (actualArgc > NUM_MANDATORY_JSFUNC_ARGS + restIndex) {
actualRestNum = actualArgc - NUM_MANDATORY_JSFUNC_ARGS - restIndex;
}
JSHandle<JSTaggedValue> restArray = JSArray::ArrayCreate(thread, JSTaggedNumber(actualRestNum));
auto argv = GetActualArgv(thread);
int idx = 0;
JSMutableHandle<JSTaggedValue> element(thread, JSTaggedValue::Undefined());
for (uint32_t i = NUM_MANDATORY_JSFUNC_ARGS + restIndex; i < actualArgc; ++i) {
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
JSTaggedType arg = reinterpret_cast<JSTaggedType *>(argv)[i];
element.Update(JSTaggedValue(arg));
JSObject::SetProperty(thread, restArray, idx++, element, true);
}
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return restArray.GetTaggedValue();
}
JSTaggedValue RuntimeStubs::RuntimeOptSuspendGenerator(JSThread *thread, const JSHandle<JSTaggedValue> &genObj,
const JSHandle<JSTaggedValue> &value)
{
if (genObj->IsAsyncGeneratorObject()) {
JSHandle<JSAsyncGeneratorObject> generatorObjectHandle(genObj);
// change state to SuspendedYield
if (generatorObjectHandle->IsExecuting()) {
return value.GetTaggedValue();
}
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return generatorObjectHandle.GetTaggedValue();
}
if (genObj->IsGeneratorObject()) {
JSHandle<JSGeneratorObject> generatorObjectHandle(genObj);
// set TaskInfo for TaskPool
generatorObjectHandle->SetTaskInfo(thread->GetTaskInfo());
// change state to SuspendedYield
if (generatorObjectHandle->IsExecuting()) {
generatorObjectHandle->SetGeneratorState(JSGeneratorState::SUSPENDED_YIELD);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return value.GetTaggedValue();
}
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return generatorObjectHandle.GetTaggedValue();
}
return RuntimeThrowTypeError(thread, "RuntimeSuspendGenerator failed");
}
JSTaggedValue RuntimeStubs::RuntimeOptAsyncGeneratorResolve(JSThread *thread, JSHandle<JSTaggedValue> asyncFuncObj,
JSHandle<JSTaggedValue> value, JSTaggedValue flag)
{
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSAsyncGeneratorObject> asyncGeneratorObjHandle(asyncFuncObj);
JSHandle<JSTaggedValue> valueHandle(value);
JSHandle<GeneratorContext> genContextHandle(thread, asyncGeneratorObjHandle->GetGeneratorContext());
ASSERT(flag.IsBoolean());
bool done = flag.IsTrue();
return JSAsyncGeneratorObject::AsyncGeneratorResolve(thread, asyncGeneratorObjHandle, valueHandle, done);
}
JSTaggedValue RuntimeStubs::RuntimeOptConstruct(JSThread *thread, JSHandle<JSTaggedValue> ctor,
JSHandle<JSTaggedValue> newTarget, JSHandle<JSTaggedValue> preArgs,
JSHandle<TaggedArray> args)
{
if (newTarget->IsUndefined()) {
newTarget = ctor;
}
if (!(newTarget->IsConstructor() && ctor->IsConstructor())) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Constructor is false", JSTaggedValue::Exception());
}
if (ctor->IsJSFunction()) {
return RuntimeOptConstructGeneric(thread, JSHandle<JSFunction>::Cast(ctor), newTarget, preArgs, args);
}
if (ctor->IsBoundFunction()) {
return RuntimeOptConstructBoundFunction(
thread, JSHandle<JSBoundFunction>::Cast(ctor), newTarget, preArgs, args);
}
if (ctor->IsJSProxy()) {
return RuntimeOptConstructProxy(thread, JSHandle<JSProxy>::Cast(ctor), newTarget, preArgs, args);
}
THROW_TYPE_ERROR_AND_RETURN(thread, "Constructor NonConstructor", JSTaggedValue::Exception());
}
JSTaggedValue RuntimeStubs::RuntimeOptConstructProxy(JSThread *thread, JSHandle<JSProxy> ctor,
JSHandle<JSTaggedValue> newTgt, JSHandle<JSTaggedValue> preArgs,
JSHandle<TaggedArray> args)
{
// step 1 ~ 4 get ProxyHandler and ProxyTarget
JSHandle<JSTaggedValue> handler(thread, ctor->GetHandler());
if (handler->IsNull()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Constructor: handler is null", JSTaggedValue::Exception());
}
ASSERT(handler->IsECMAObject());
JSHandle<JSTaggedValue> target(thread, ctor->GetTarget());
// 5.Let trap be GetMethod(handler, "construct").
JSHandle<JSTaggedValue> key(thread->GlobalConstants()->GetHandledProxyConstructString());
JSHandle<JSTaggedValue> method = JSObject::GetMethod(thread, handler, key);
// 6.ReturnIfAbrupt(trap).
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 7.If trap is undefined, then
// a.Assert: target has a [[Construct]] internal method.
// b.Return Construct(target, argumentsList, newTarget).
if (method->IsUndefined()) {
ASSERT(target->IsConstructor());
return RuntimeOptConstruct(thread, target, newTgt, preArgs, args);
}
// 8.Let argArray be CreateArrayFromList(argumentsList).
uint32_t preArgsSize = preArgs->IsUndefined() ? 0 : JSHandle<TaggedArray>::Cast(preArgs)->GetLength();
const uint32_t argsCount = args->GetLength();
const uint32_t size = preArgsSize + argsCount;
JSHandle<TaggedArray> arr = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(size);
if (preArgsSize > 0) {
JSHandle<TaggedArray> tgaPreArgs = JSHandle<TaggedArray>::Cast(preArgs);
for (uint32_t i = 0; i < preArgsSize; ++i) {
JSTaggedValue value = tgaPreArgs->Get(i);
arr->Set(thread, i, value);
}
}
for (uint32_t i = 0; i < argsCount; ++i) {
JSTaggedValue value = args->Get(i);
arr->Set(thread, i + preArgsSize, value);
}
JSHandle<JSArray> newArr = JSArray::CreateArrayFromList(thread, arr);
// step 8 ~ 9 Call(trap, handler, «target, argArray, newTarget »).
const uint32_t argsLength = 3; // 3: «target, argArray, newTarget »
JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, method, handler, undefined, argsLength);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(target.GetTaggedValue(), newArr.GetTaggedValue(), newTgt.GetTaggedValue());
JSTaggedValue newObjValue = JSFunction::Call(info);
// 10.ReturnIfAbrupt(newObj).
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 11.If Type(newObj) is not Object, throw a TypeError exception.
if (!newObjValue.IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "new object is not object", JSTaggedValue::Exception());
}
// 12.Return newObj.
return newObjValue;
}
JSTaggedValue RuntimeStubs::RuntimeOptConstructBoundFunction(JSThread *thread, JSHandle<JSBoundFunction> ctor,
JSHandle<JSTaggedValue> newTgt,
JSHandle<JSTaggedValue> preArgs,
JSHandle<TaggedArray> args)
{
JSHandle<JSTaggedValue> target(thread, ctor->GetBoundTarget());
if (!target->IsConstructor()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Constructor is false", JSTaggedValue::Exception());
}
JSHandle<TaggedArray> boundArgs(thread, ctor->GetBoundArguments());
JSMutableHandle<JSTaggedValue> newPreArgs(thread, preArgs.GetTaggedValue());
if (newPreArgs->IsUndefined()) {
newPreArgs.Update(boundArgs.GetTaggedValue());
} else {
newPreArgs.Update(
TaggedArray::Append(thread, boundArgs, JSHandle<TaggedArray>::Cast(preArgs)).GetTaggedValue());
}
JSMutableHandle<JSTaggedValue> newTargetMutable(thread, newTgt.GetTaggedValue());
if (JSTaggedValue::SameValue(ctor.GetTaggedValue(), newTgt.GetTaggedValue())) {
newTargetMutable.Update(target.GetTaggedValue());
}
return RuntimeOptConstruct(thread, target, newTargetMutable, newPreArgs, args);
}
JSTaggedValue RuntimeStubs::GetResultValue(JSThread *thread, bool isAotMethod, JSHandle<JSFunction> ctor,
CVector<JSTaggedType> &values, JSHandle<JSTaggedValue> newTgt, uint32_t &size, JSHandle<JSTaggedValue> obj)
{
JSTaggedValue resultValue;
if (isAotMethod && ctor->IsClassConstructor()) {
uint32_t numArgs = ctor->GetCallTarget()->GetNumArgsWithCallField();
bool needPushArgv = numArgs != size;
const JSTaggedType *prevFp = thread->GetLastLeaveFrame();
if (ctor->GetCallTarget()->IsFastCall()) {
if (needPushArgv) {
values.reserve(numArgs + NUM_MANDATORY_JSFUNC_ARGS - 1);
for (uint32_t i = size; i < numArgs; i++) {
values.emplace_back(JSTaggedValue::VALUE_UNDEFINED);
}
size = numArgs;
}
resultValue = thread->GetEcmaVM()->FastCallAot(size, values.data(), prevFp);
} else {
resultValue = thread->GetCurrentEcmaContext()->ExecuteAot(size, values.data(), prevFp, needPushArgv);
}
} else {
ctor->GetCallTarget()->SetAotCodeBit(false); // if Construct is not ClassConstructor, don't run aot
EcmaRuntimeCallInfo *info =
EcmaInterpreter::NewRuntimeCallInfo(thread, JSHandle<JSTaggedValue>(ctor), obj, newTgt, size);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(size, values.data());
resultValue = EcmaInterpreter::Execute(info);
}
return resultValue;
}
JSTaggedValue RuntimeStubs::RuntimeOptConstructGeneric(JSThread *thread, JSHandle<JSFunction> ctor,
JSHandle<JSTaggedValue> newTgt,
JSHandle<JSTaggedValue> preArgs, JSHandle<TaggedArray> args)
{
if (!ctor->IsConstructor()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Constructor is false", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> obj(thread, JSTaggedValue::Undefined());
if (ctor->IsBase()) {
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
obj = JSHandle<JSTaggedValue>(factory->NewJSObjectByConstructor(ctor, newTgt));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
uint32_t preArgsSize = preArgs->IsUndefined() ? 0 : JSHandle<TaggedArray>::Cast(preArgs)->GetLength();
const uint32_t argsCount = args->GetLength();
uint32_t size = preArgsSize + argsCount;
CVector<JSTaggedType> values;
Method *method = ctor->GetCallTarget();
bool isAotMethod = method->IsAotWithCallField();
if (!thread->IsWorker() && isAotMethod && ctor->IsClassConstructor()) {
if (method->IsFastCall()) {
values.reserve(size + NUM_MANDATORY_JSFUNC_ARGS - 1);
values.emplace_back(ctor.GetTaggedValue().GetRawData());
values.emplace_back(obj.GetTaggedValue().GetRawData());
} else {
values.reserve(size + NUM_MANDATORY_JSFUNC_ARGS);
values.emplace_back(ctor.GetTaggedValue().GetRawData());
values.emplace_back(newTgt.GetTaggedValue().GetRawData());
values.emplace_back(obj.GetTaggedValue().GetRawData());
}
} else {
values.reserve(size);
}
if (preArgsSize > 0) {
JSHandle<TaggedArray> tgaPreArgs = JSHandle<TaggedArray>::Cast(preArgs);
for (uint32_t i = 0; i < preArgsSize; ++i) {
JSTaggedValue value = tgaPreArgs->Get(i);
values.emplace_back(value.GetRawData());
}
for (uint32_t i = 0; i < argsCount; ++i) {
values.emplace_back(args->Get(i).GetRawData());
}
} else {
for (uint32_t i = 0; i < argsCount; ++i) {
values.emplace_back(args->Get(i).GetRawData());
}
}
JSTaggedValue resultValue = RuntimeStubs::GetResultValue(thread, isAotMethod, ctor, values, newTgt, size, obj);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 9.3.2 [[Construct]] (argumentsList, newTarget)
if (resultValue.IsECMAObject()) {
if (resultValue.IsJSShared()) {
JSObject::Cast(resultValue.GetTaggedObject())->GetJSHClass()->SetExtensible(false);
}
return resultValue;
}
if (ctor->IsBase()) {
return obj.GetTaggedValue();
}
if (!resultValue.IsUndefined()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "function is non-constructor", JSTaggedValue::Exception());
}
return obj.GetTaggedValue();
}
JSTaggedValue RuntimeStubs::RuntimeOptNewObjRange(JSThread *thread, uintptr_t argv, uint32_t argc)
{
JSHandle<JSTaggedValue> ctor = GetHArg<JSTaggedValue>(argv, argc, 0);
JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
const size_t firstArgOffset = 1;
ASSERT(argc > 0);
size_t arrLength = argc - firstArgOffset;
JSHandle<TaggedArray> args = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(arrLength);
for (size_t i = 0; i < arrLength; ++i) {
args->Set(thread, i, GetArg(argv, argc, i + firstArgOffset));
}
JSTaggedValue object = RuntimeOptConstruct(thread, ctor, ctor, undefined, args);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (!object.IsUndefined() && !object.IsECMAObject() && !JSHandle<JSFunction>(ctor)->IsBase()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Derived constructor must return object or undefined",
JSTaggedValue::Exception());
}
return object;
}
JSTaggedValue RuntimeStubs::RuntimeOptGenerateScopeInfo(JSThread *thread, uint16_t scopeId, JSTaggedValue func)
{
EcmaVM *ecmaVm = thread->GetEcmaVM();
ObjectFactory *factory = ecmaVm->GetFactory();
Method *method = ECMAObject::Cast(func.GetTaggedObject())->GetCallTarget();
const JSPandaFile *jsPandaFile = method->GetJSPandaFile();
JSHandle<ConstantPool> constpool(thread, method->GetConstantPool());
panda_file::File::EntityId id = constpool->GetEntityId(scopeId);
JSHandle<TaggedArray> elementsLiteral =
LiteralDataExtractor::GetDatasIgnoreType(thread, jsPandaFile, id, constpool);
ASSERT(elementsLiteral->GetLength() > 0);
auto *scopeDebugInfo = ecmaVm->GetNativeAreaAllocator()->New<ScopeDebugInfo>();
if (scopeDebugInfo == nullptr) {
return JSTaggedValue::Hole();
}
size_t length = elementsLiteral->GetLength();
for (size_t i = 1; i < length; i += 2) { // 2: Each literal buffer contains a pair of key-value.
JSTaggedValue val = elementsLiteral->Get(i);
ASSERT(val.IsString());
CString name = ConvertToString(EcmaString::Cast(val.GetTaggedObject()));
int32_t slot = elementsLiteral->Get(i + 1).GetInt();
scopeDebugInfo->scopeInfo.emplace(name, slot);
}
JSHandle<JSNativePointer> pointer = factory->NewJSNativePointer(
scopeDebugInfo, NativeAreaAllocator::FreeObjectFunc<ScopeDebugInfo>, ecmaVm->GetNativeAreaAllocator());
return pointer.GetTaggedValue();
}
JSTaggedType *RuntimeStubs::GetActualArgv(JSThread *thread)
{
JSTaggedType *current = const_cast<JSTaggedType *>(thread->GetLastLeaveFrame());
FrameIterator it(current, thread);
ASSERT(it.IsLeaveFrame());
it.Advance<GCVisitedFlag::VISITED>();
ASSERT(it.IsAotOrJitFunctionFrame());
if (it.IsFastJitFunctionFrame()) {
auto optimizedJSJITFunctionFrame = it.GetFrame<FASTJITFunctionFrame>();
return optimizedJSJITFunctionFrame->GetArgv(it);
} else {
auto optimizedJSFunctionFrame = it.GetFrame<OptimizedJSFunctionFrame>();
return optimizedJSFunctionFrame->GetArgv(it);
}
}
JSTaggedType *RuntimeStubs::GetActualArgvFromStub(JSThread *thread)
{
JSTaggedType *current = const_cast<JSTaggedType *>(thread->GetLastLeaveFrame());
FrameIterator it(current, thread);
ASSERT(it.IsLeaveFrame());
it.Advance<GCVisitedFlag::VISITED>();
ASSERT(it.IsOptimizedFrame());
it.Advance<GCVisitedFlag::VISITED>();
ASSERT(it.IsAotOrJitFunctionFrame());
if (it.IsFastJitFunctionFrame()) {
auto optimizedJSJITFunctionFrame = it.GetFrame<FASTJITFunctionFrame>();
return optimizedJSJITFunctionFrame->GetArgv(it);
}
auto optimizedJSFunctionFrame = it.GetFrame<OptimizedJSFunctionFrame>();
return optimizedJSFunctionFrame->GetArgv(it);
}
OptimizedJSFunctionFrame *RuntimeStubs::GetOptimizedJSFunctionFrame(JSThread *thread)
{
JSTaggedType *current = const_cast<JSTaggedType *>(thread->GetLastLeaveFrame());
FrameIterator it(current, thread);
ASSERT(it.IsLeaveFrame());
it.Advance();
ASSERT(it.IsOptimizedJSFunctionFrame());
return it.GetFrame<OptimizedJSFunctionFrame>();
}
OptimizedJSFunctionFrame *RuntimeStubs::GetOptimizedJSFunctionFrameNoGC(JSThread *thread)
{
JSTaggedType *current = const_cast<JSTaggedType *>(thread->GetLastLeaveFrame());
FrameIterator it(current, thread);
ASSERT(it.IsOptimizedFrame());
it.Advance();
ASSERT(it.IsOptimizedJSFunctionFrame());
return it.GetFrame<OptimizedJSFunctionFrame>();
}
JSTaggedValue RuntimeStubs::RuntimeLdPatchVar(JSThread *thread, uint32_t index)
{
JSHandle<TaggedArray> globalPatch =
JSHandle<TaggedArray>::Cast(thread->GetEcmaVM()->GetGlobalEnv()->GetGlobalPatch());
return globalPatch->Get(thread, index);
}
JSTaggedValue RuntimeStubs::RuntimeStPatchVar(JSThread *thread, uint32_t index, const JSHandle<JSTaggedValue> &value)
{
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
JSHandle<TaggedArray> globalPatch = JSHandle<TaggedArray>::Cast(env->GetGlobalPatch());
if (index >= globalPatch->GetLength()) {
globalPatch = TaggedArray::SetCapacity(thread, globalPatch, index + 1);
}
globalPatch->Set(thread, index, value);
env->SetGlobalPatch(thread, globalPatch.GetTaggedValue());
return JSTaggedValue::True();
}
JSTaggedValue RuntimeStubs::RuntimeNotifyConcurrentResult(JSThread *thread, JSTaggedValue result, JSTaggedValue hint)
{
thread->GetEcmaVM()->TriggerConcurrentCallback(result, hint);
return JSTaggedValue::Undefined();
}
bool RuntimeStubs::IsNeedNotifyHclassChangedForAotTransition(JSThread *thread, const JSHandle<JSHClass> &hclass,
JSTaggedValue key)
{
JSMutableHandle<JSObject> protoHandle(thread, hclass->GetPrototype());
while (true) {
if (!protoHandle.GetTaggedValue().IsHeapObject()) {
break;
}
JSHClass *protoHclass = protoHandle->GetJSHClass();
if (JSHClass::FindPropertyEntry(thread, protoHclass, key) != -1) {
return true;
}
protoHandle.Update(protoHclass->GetPrototype());
}
return false;
}
JSTaggedValue RuntimeStubs::RuntimeUpdateAOTHClass(JSThread *thread,
const JSHandle<JSHClass> &oldhclass, const JSHandle<JSHClass> &newhclass, JSTaggedValue key)
{
#if ECMASCRIPT_ENABLE_IC
if (IsNeedNotifyHclassChangedForAotTransition(thread, oldhclass, key)) {
JSHClass::NotifyHclassChanged(thread, oldhclass, newhclass, key);
}
JSHClass::RefreshUsers(thread, oldhclass, newhclass);
JSHClass::EnablePHCProtoChangeMarker(thread, newhclass);
#endif
return JSTaggedValue::Undefined();
}
JSTaggedValue RuntimeStubs::RuntimeDefineField(JSThread *thread, JSTaggedValue obj,
JSTaggedValue propKey, JSTaggedValue value)
{
// Do the same thing as Object.defineProperty(obj, propKey, {value: value,
// writable:true, enumerable: true, configurable: true})
if (!obj.IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "DefineField: obj is not Object", JSTaggedValue::Exception());
}
JSHandle<JSObject> handleObj(thread, obj);
JSHandle<JSTaggedValue> handleValue(thread, value);
JSHandle<JSTaggedValue> handleKey = JSTaggedValue::ToPropertyKey(thread, JSHandle<JSTaggedValue>(thread, propKey));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSObject::CreateDataPropertyOrThrow(thread, handleObj, handleKey, handleValue, SCheckMode::SKIP);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return JSTaggedValue::Undefined();
}
JSTaggedValue RuntimeStubs::RuntimeCreatePrivateProperty(JSThread *thread, JSTaggedValue lexicalEnv,
uint32_t count, JSTaggedValue constpool, uint32_t literalId, JSTaggedValue module)
{
ObjectFactory* factory = thread->GetEcmaVM()->GetFactory();
JSHandle<LexicalEnv> handleLexicalEnv(thread, lexicalEnv);
JSHandle<ConstantPool> handleConstpool(thread, constpool);
JSHandle<JSTaggedValue> handleModule(thread, module);
JSHandle<ConstantPool> unsharedConstpoolHandle(
thread, thread->GetCurrentEcmaContext()->FindOrCreateUnsharedConstpool(constpool));
CString entry = ModuleManager::GetRecordName(handleModule.GetTaggedValue());
uint32_t length = handleLexicalEnv->GetLength() - LexicalEnv::RESERVED_ENV_LENGTH;
uint32_t startIndex = 0;
while (startIndex < length && !handleLexicalEnv->GetProperties(startIndex).IsHole()) {
startIndex++;
}
JSTaggedValue aotSymbolInfo = unsharedConstpoolHandle->GetAotSymbolInfo();
JSHandle<TaggedArray> aotSymbolInfoHandle(thread, aotSymbolInfo);
FrameHandler frameHandler(thread);
uint32_t abcId = frameHandler.GetAbcId();
ASSERT(startIndex + count <= length);
for (uint32_t i = 0; i < count; i++) {
auto index = startIndex + i;
uint64_t id = JSSymbol::GeneratePrivateId(abcId, literalId, index);
JSHandle<JSSymbol> symbolHandle;
JSTaggedValue symbol = ConstantPool::GetSymbolFromSymbolInfo(aotSymbolInfoHandle, id);
if (ConstantPool::IsAotSymbolInfoExist(aotSymbolInfoHandle, symbol)) {
symbolHandle = JSHandle<JSSymbol>(thread, symbol);
} else {
symbolHandle = factory->NewJSSymbol();
symbolHandle->SetPrivateId(id);
}
handleLexicalEnv->SetProperties(thread, index, symbolHandle.GetTaggedValue());
}
JSTaggedValue literalObj =
ConstantPool::GetClassLiteralFromCache(thread, unsharedConstpoolHandle, literalId, entry);
JSHandle<ClassLiteral> classLiteral(thread, literalObj);
JSHandle<TaggedArray> literalBuffer(thread, classLiteral->GetArray());
uint32_t literalBufferLength = literalBuffer->GetLength();
if (literalBufferLength == 0) {
return JSTaggedValue::Undefined();
}
// instace property number is hidden in the last index of literal buffer
uint32_t instacePropertyCount = static_cast<uint32_t>(literalBuffer->Get(literalBufferLength - 1).GetInt());
ASSERT(startIndex + count + literalBufferLength - (instacePropertyCount == 0) <= length);
for (uint32_t i = 0; i < literalBufferLength - 1; i++) {
JSTaggedValue literalValue = literalBuffer->Get(i);
if (LIKELY(literalValue.IsJSFunction())) {
JSFunction *func = JSFunction::Cast(literalValue.GetTaggedObject());
func->SetLexicalEnv(thread, handleLexicalEnv.GetTaggedValue());
func->SetModule(thread, handleModule.GetTaggedValue());
}
handleLexicalEnv->SetProperties(thread, startIndex + count + i, literalValue);
}
if (instacePropertyCount > 0) {
auto index = startIndex + count + literalBufferLength - 1;
uint64_t id = JSSymbol::GeneratePrivateId(abcId, literalId, index);
JSHandle<JSSymbol> symbolHandle;
JSTaggedValue symbol = ConstantPool::GetSymbolFromSymbolInfo(aotSymbolInfoHandle, id);
if (ConstantPool::IsAotSymbolInfoExist(aotSymbolInfoHandle, symbol)) {
symbolHandle = JSHandle<JSSymbol>(thread, symbol);
} else {
symbolHandle = factory->NewPublicSymbolWithChar("method");
symbolHandle->SetPrivateId(id);
}
handleLexicalEnv->SetProperties(thread, index, symbolHandle.GetTaggedValue());
}
return JSTaggedValue::Undefined();
}
JSTaggedValue RuntimeStubs::RuntimeDefinePrivateProperty(JSThread *thread, JSTaggedValue lexicalEnv,
uint32_t levelIndex, uint32_t slotIndex, JSTaggedValue obj, JSTaggedValue value)
{
JSTaggedValue currentLexicalEnv = lexicalEnv;
for (uint32_t i = 0; i < levelIndex; i++) {
currentLexicalEnv = LexicalEnv::Cast(currentLexicalEnv.GetTaggedObject())->GetParentEnv();
ASSERT(!currentLexicalEnv.IsUndefined());
}
JSTaggedValue key = LexicalEnv::Cast(currentLexicalEnv.GetTaggedObject())->GetProperties(slotIndex);
// private property is invisible for proxy
JSHandle<JSTaggedValue> handleObj(thread, obj.IsJSProxy() ? JSProxy::Cast(obj)->GetPrivateField() : obj);
JSHandle<JSTaggedValue> handleKey(thread, key);
JSHandle<JSTaggedValue> handleValue(thread, value);
PropertyDescriptor desc(thread);
if (!JSTaggedValue::IsPropertyKey(handleKey) &&
JSTaggedValue::GetOwnProperty(thread, handleObj, handleKey, desc)) {
THROW_TYPE_ERROR_AND_RETURN(thread, "invalid private key or already exists", JSTaggedValue::Exception());
}
bool extensible = handleObj->IsExtensible(thread);
if (handleObj->IsUndefined()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "DefinePrivateProperty obj is undefined", JSTaggedValue::Exception());
}
if (!extensible) {
// private key should be always extensible
handleObj->GetTaggedObject()->GetClass()->SetExtensible(true);
}
bool result = JSObject::CreateDataPropertyOrThrow(thread, JSHandle<JSObject>::Cast(handleObj),
handleKey, handleValue);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (!extensible) {
handleObj->GetTaggedObject()->GetClass()->SetExtensible(false);
}
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return JSTaggedValue(result);
}
JSTaggedValue RuntimeStubs::RuntimeNotifyDebuggerStatement(JSThread *thread)
{
FrameHandler frameHandler(thread);
for (; frameHandler.HasFrame(); frameHandler.PrevJSFrame()) {
if (frameHandler.IsEntryFrame() || frameHandler.IsBuiltinFrame()) {
continue;
}
Method *method = frameHandler.GetMethod();
if (method->IsNativeWithCallField()) {
continue;
}
auto bcOffset = frameHandler.GetBytecodeOffset();
auto *debuggerMgr = thread->GetEcmaVM()->GetJsDebuggerManager();
debuggerMgr->GetNotificationManager()->DebuggerStmtEvent(thread, method, bcOffset);
return JSTaggedValue::Hole();
}
return JSTaggedValue::Hole();
}
bool RuntimeStubs::CheckElementsNumber(JSHandle<TaggedArray> elements, uint32_t len)
{
ASSERT(len <= elements->GetLength());
for (uint32_t i = 0; i < len; i++) {
if (!elements->Get(i).IsNumber()) {
return false;
}
}
return true;
}
JSHandle<JSTaggedValue> RuntimeStubs::GetOrCreateNumberString(JSThread *thread, JSHandle<JSTaggedValue> presentValue,
std::map<uint64_t, JSHandle<JSTaggedValue>> &cachedString)
{
JSMutableHandle<JSTaggedValue> presentString(thread, JSTaggedValue::Undefined());
auto iter = cachedString.find(presentValue->GetRawData());
if (iter != cachedString.end()) {
presentString.Update(iter->second);
} else {
presentString.Update(JSTaggedValue::ToString(thread, presentValue).GetTaggedValue());
cachedString[presentValue->GetRawData()] = presentString;
}
return presentString;
}
JSTaggedValue RuntimeStubs::TryCopyCOWArray(JSThread *thread, JSHandle<JSArray> holderHandler, bool &isCOWArray)
{
if (isCOWArray) {
JSArray::CheckAndCopyArray(thread, holderHandler);
isCOWArray = false;
}
return holderHandler->GetElements();
}
JSTaggedValue RuntimeStubs::ArrayNumberSort(JSThread *thread, JSHandle<JSObject> thisObj, uint32_t len)
{
JSMutableHandle<JSTaggedValue> presentValue(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> middleValue(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> previousValue(thread, JSTaggedValue::Undefined());
bool isCOWArray = JSHandle<JSTaggedValue>(thisObj)->IsJSCOWArray();
JSMutableHandle<TaggedArray> elements(thread, thisObj->GetElements());
std::map<uint64_t, JSHandle<JSTaggedValue>> cachedString;
for (uint32_t i = 1; i < len; i++) {
uint32_t beginIndex = 0;
uint32_t endIndex = i;
presentValue.Update(elements->Get(i));
JSHandle<JSTaggedValue> presentString = GetOrCreateNumberString(thread, presentValue, cachedString);
while (beginIndex < endIndex) {
uint32_t middleIndex = beginIndex + (endIndex - beginIndex) / 2; // 2 : half
middleValue.Update(elements->Get(middleIndex));
JSHandle<JSTaggedValue> middleString = GetOrCreateNumberString(thread, middleValue, cachedString);
double compareResult = ArrayHelper::StringSortCompare(thread, middleString, presentString);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
if (compareResult > 0) {
endIndex = middleIndex;
} else {
beginIndex = middleIndex + 1;
}
}
if (endIndex >= 0 && endIndex < i) {
for (uint32_t j = i; j > endIndex; j--) {
previousValue.Update(elements->Get(j - 1));
elements.Update(TryCopyCOWArray(thread, JSHandle<JSArray>(thisObj), isCOWArray));
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
elements->Set(thread, j, previousValue);
}
elements.Update(TryCopyCOWArray(thread, JSHandle<JSArray>(thisObj), isCOWArray));
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
elements->Set(thread, endIndex, presentValue);
}
}
return thisObj.GetTaggedValue();
}
JSTaggedType RuntimeStubs::RuntimeTryGetInternString(uintptr_t argGlue, const JSHandle<EcmaString> &string)
{
auto thread = JSThread::GlueToJSThread(argGlue);
EcmaString *str =
thread->GetEcmaVM()->GetEcmaStringTable()->TryGetInternString(thread, string);
if (str == nullptr) {
return JSTaggedValue::Hole().GetRawData();
}
return JSTaggedValue::Cast(static_cast<void *>(str));
}
uint32_t RuntimeStubs::RuntimeGetBytecodePcOfstForBaseline(const JSHandle<JSFunction> &func, uintptr_t nativePc)
{
// Compute current bytecodePc according to nativePc of returnAddress
LOG_BASELINEJIT(DEBUG) << "nativePc address: " << std::hex << nativePc;
const MachineCode *machineCode = MachineCode::Cast(func->GetBaselineCode().GetTaggedObject());
const uintptr_t nativePcStart = machineCode->GetFuncAddr();
LOG_BASELINEJIT(DEBUG) << "baselineCode nativeStart address: " << std::hex << nativePcStart;
const Method *thisMethod = Method::Cast(func->GetMethod().GetTaggedObject());
const uint8_t *bytecodeStart = thisMethod->GetBytecodeArray();
const uint8_t *bytecodeEnd = bytecodeStart + thisMethod->GetCodeSize();
LOG_BASELINEJIT(DEBUG) << "bytecodePc start: " << reinterpret_cast<uintptr_t>(bytecodeStart);
LOG_BASELINEJIT(DEBUG) << "bytecodePc end: " << reinterpret_cast<uintptr_t>(bytecodeEnd);
const uint8_t *offsetTableAddr = machineCode->GetStackMapOrOffsetTableAddress();
const uint32_t offsetTableSize = machineCode->GetStackMapOrOffsetTableSize();
uintptr_t nativePcEnd = nativePcStart;
uint32_t pcOffsetIndex = 0;
auto opcode = kungfu::Bytecodes::GetOpcode(bytecodeStart);
while (nativePcEnd < nativePc && pcOffsetIndex < offsetTableSize) {
nativePcEnd += static_cast<uintptr_t>(offsetTableAddr[pcOffsetIndex++]);
opcode = kungfu::Bytecodes::GetOpcode(bytecodeStart);
bytecodeStart += BytecodeInstruction::Size(opcode);
}
// Since the nativePc is returnAddress, we need to take the previous bytecode
bytecodeStart -= BytecodeInstruction::Size(opcode);
if (nativePcEnd < nativePc) {
LOG_ECMA(FATAL) <<
"invalid nativePc or pcOffsetTable for getting bytecode pc in baseline code, the nativePcEnd is " <<
std::hex << nativePcEnd;
}
if (bytecodeStart > bytecodeEnd) {
LOG_ECMA(FATAL) <<
"out of bytecodeArray range for getting bytecode pc in baseline code, the bytecodePc is " <<
reinterpret_cast<uintptr_t>(bytecodeStart);
}
auto bytecodePcOffset = static_cast<uint32_t>(bytecodeStart - thisMethod->GetBytecodeArray());
LOG_BASELINEJIT(DEBUG) << "current bytecodePc offset: " << bytecodePcOffset;
return bytecodePcOffset;
}
} // namespace panda::ecmascript
#endif // ECMASCRIPT_STUBS_RUNTIME_STUBS_INL_H