diff --git a/BUILD.gn b/BUILD.gn index 2240b29b852d7ba6e2a086a85bafa040c9c4a30c..76ee6e0d1071582d0ed7a86a5bcd33d8af444796 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -801,6 +801,7 @@ ecma_source = [ "ecmascript/regexp/regexp_opcode.cpp", "ecmascript/regexp/regexp_parser.cpp", "ecmascript/regexp/regexp_parser_cache.cpp", + "ecmascript/global_index_map.cpp", "ecmascript/shared_mm/shared_mm.cpp", "ecmascript/tagged_dictionary.cpp", "ecmascript/tagged_hash_array.cpp", diff --git a/ecmascript/base/error_helper.cpp b/ecmascript/base/error_helper.cpp index 58197f6ac753f44874f67a3e74fb364636ddc48a..18b32e6f952aa9e71d8369f49a4e62e7c7a11981 100644 --- a/ecmascript/base/error_helper.cpp +++ b/ecmascript/base/error_helper.cpp @@ -131,7 +131,7 @@ JSHandle ErrorHelper::GetErrorName(JSThread *thread, const JSHand } JSTaggedValue ErrorHelper::ErrorCommonConstructor(EcmaRuntimeCallInfo *argv, - const ErrorType &errorType) + [[maybe_unused]] const ErrorType &errorType) { JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); @@ -195,8 +195,7 @@ JSTaggedValue ErrorHelper::ErrorCommonConstructor(EcmaRuntimeCallInfo *argv, ASSERT_PRINT(status == true, "return result exception!"); } - bool isOOMError = errorType == ErrorType::OOM_ERROR; - JSHandle handleStack = BuildEcmaStackTrace(thread, isOOMError); + JSHandle handleStack = BuildEcmaStackTrace(thread); JSHandle stackkey = globalConst->GetHandledStackString(); PropertyDescriptor stackDesc(thread, JSHandle::Cast(handleStack), true, false, true); [[maybe_unused]] bool status = JSObject::DefineOwnProperty(thread, nativeInstanceObj, stackkey, stackDesc); @@ -225,11 +224,15 @@ JSHandle ErrorHelper::GetErrorJSFunction(JSThread *thread) return thread->GlobalConstants()->GetHandledUndefined(); } -JSHandle ErrorHelper::BuildEcmaStackTrace(JSThread *thread, bool isOOMError) +JSHandle ErrorHelper::BuildEcmaStackTrace(JSThread *thread) { std::string data = JsStackInfo::BuildJsStackTrace(thread, false); - if (isOOMError) { - data = data.substr(0, MAX_ERROR_SIZE); + if (data.size() > MAX_ERROR_SIZE) { + // find last line break from 0 to MAX_ERROR_SIZE + size_t pos = data.rfind('\n', MAX_ERROR_SIZE); + if (pos != std::string::npos) { + data = data.substr(0, pos); + } } ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); LOG_ECMA(DEBUG) << data; diff --git a/ecmascript/base/error_helper.h b/ecmascript/base/error_helper.h index aedf378e641f6844fa52921971da955da3aa5f7f..c2917c265c817aff080b13860584e695c251d5a5 100644 --- a/ecmascript/base/error_helper.h +++ b/ecmascript/base/error_helper.h @@ -35,11 +35,11 @@ public: private: static JSHandle GetErrorJSFunction(JSThread *thread); - static JSHandle BuildEcmaStackTrace(JSThread *thread, bool isOOMError); + static JSHandle BuildEcmaStackTrace(JSThread *thread); static JSHandle GetErrorName(JSThread *thread, const JSHandle &name, const ErrorType &errorType); - static constexpr uint32_t MAX_ERROR_SIZE = 128_KB; + static constexpr uint32_t MAX_ERROR_SIZE = 10_KB; }; } // namespace panda::ecmascript::base diff --git a/ecmascript/base/json_parser.h b/ecmascript/base/json_parser.h index bf21ab47b7380f819b39a3386f96b9e80533cd38..c280d0a265a088d8b4151839a16244b109ec9c84 100644 --- a/ecmascript/base/json_parser.h +++ b/ecmascript/base/json_parser.h @@ -53,10 +53,23 @@ enum class Tokens : uint8_t { TOKEN_ILLEGAL, }; +struct JsonContinuation { + enum class ContinuationType : uint8_t { + RETURN = 0, + ARRAY, + OBJECT, + }; + JsonContinuation(ContinuationType type, size_t index) : type_(type), index_(index) {} + + ContinuationType type_ {ContinuationType::RETURN}; + size_t index_ {0}; +}; + template class JsonParser { protected: using Text = const T *; + using ContType = JsonContinuation::ContinuationType; // Instantiation of the class is prohibited JsonParser() = default; explicit JsonParser(JSThread *thread) : thread_(thread) {} @@ -78,6 +91,11 @@ protected: auto vm = thread_->GetEcmaVM(); factory_ = vm->GetFactory(); env_ = *vm->GetGlobalEnv(); + JSHandle arrayFunc(env_->GetArrayFunction()); + initialJSArrayClass_ = JSHandle(thread_, JSFunction::GetOrCreateInitialJSHClass(thread_, arrayFunc)); + JSHandle objectFunc(env_->GetObjectFunction()); + initialJSObjectClass_ = + JSHandle(thread_, JSFunction::GetOrCreateInitialJSHClass(thread_, objectFunc)); SkipEndWhiteSpace(); range_ = end_; @@ -85,28 +103,195 @@ protected: return JSHandle(thread_, result); } - JSTaggedValue ParseJSONText(bool inObjorArr = false) + inline bool IsInObjOrArray(ContType type) + { + return type == ContType::ARRAY || type == ContType::OBJECT; + } + + inline bool EmptyArrayCheck() + { + GetNextNonSpaceChar(); + return *current_ == ']'; + } + + inline bool EmptyObjectCheck() + { + GetNextNonSpaceChar(); + return *current_ == '}'; + } + + JSTaggedValue ParseJSONText() + { + JSHandle parseValue; + std::vector continuationList; + std::vector> elementsList; + std::vector> propertyList; + continuationList.reserve(16); // 16: initial capacity + elementsList.reserve(16); // 16: initial capacity + propertyList.reserve(16); // 16: initial capacity + JsonContinuation continuation(ContType::RETURN, 0); + while (true) { + while (true) { + SkipStartWhiteSpace(); + Tokens token = ParseToken(); + switch (token) { + case Tokens::OBJECT: + if (EmptyObjectCheck()) { + parseValue = JSHandle(factory_->NewJSObject(initialJSObjectClass_)); + GetNextNonSpaceChar(); + break; + } + continuationList.emplace_back(std::move(continuation)); + continuation = JsonContinuation(ContType::OBJECT, propertyList.size()); + + SkipStartWhiteSpace(); + if (*current_ == '"') { + propertyList.emplace_back(JSHandle(thread_, ParseString(true))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); + } else { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Object Prop in JSON", + JSTaggedValue::Exception()); + } + SkipStartWhiteSpace(); + if (*current_ == ':') { + Advance(); + } else { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Object in JSON", + JSTaggedValue::Exception()); + } + continue; + case Tokens::ARRAY: + if (EmptyArrayCheck()) { + parseValue = JSHandle(factory_->NewJSArray(0, initialJSArrayClass_)); + GetNextNonSpaceChar(); + break; + } + continuationList.emplace_back(std::move(continuation)); + continuation = JsonContinuation(ContType::ARRAY, elementsList.size()); + continue; + case Tokens::LITERAL_TRUE: + parseValue = JSHandle(thread_, ParseLiteralTrue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); + break; + case Tokens::LITERAL_FALSE: + parseValue = JSHandle(thread_, ParseLiteralFalse()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); + break; + case Tokens::LITERAL_NULL: + parseValue = JSHandle(thread_, ParseLiteralNull()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); + break; + case Tokens::NUMBER: + parseValue = JSHandle(thread_, ParseNumber(IsInObjOrArray(continuation.type_))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); + break; + case Tokens::STRING: + parseValue = JSHandle(thread_, ParseString(IsInObjOrArray(continuation.type_))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); + break; + default: + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON", JSTaggedValue::Exception()); + } + break; + } + + while (true) { + switch (continuation.type_) { + case ContType::RETURN: + ASSERT(continuationList.empty()); + ASSERT(elementsList.empty()); + ASSERT(propertyList.empty()); + if (current_ <= range_) { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON", + JSTaggedValue::Exception()); + } + return parseValue.GetTaggedValue(); + case ContType::ARRAY: { + elementsList.emplace_back(parseValue); + SkipStartWhiteSpace(); + if (*current_ == ',') { + Advance(); + break; + } + parseValue = CreateJsonArray(continuation, elementsList); + if (*current_ != ']') { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Array in JSON", + JSTaggedValue::Exception()); + } + GetNextNonSpaceChar(); + elementsList.resize(continuation.index_); + continuation = std::move(continuationList.back()); + continuationList.pop_back(); + continue; + } + case ContType::OBJECT: { + propertyList.emplace_back(parseValue); + SkipStartWhiteSpace(); + if (*current_ == ',') { + GetNextNonSpaceChar(); + if (*current_ == '"') { + propertyList.emplace_back(JSHandle(thread_, ParseString(true))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); + } else { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Object Prop in JSON", + JSTaggedValue::Exception()); + } + SkipStartWhiteSpace(); + if (*current_ == ':') { + Advance(); + } else { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Object in JSON", + JSTaggedValue::Exception()); + } + break; + } + + parseValue = CreateJsonObject(continuation, propertyList); + if (*current_ == '}') { + GetNextNonSpaceChar(); + } else { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Object in JSON", + JSTaggedValue::Exception()); + } + propertyList.resize(continuation.index_); + continuation = std::move(continuationList.back()); + continuationList.pop_back(); + continue; + } + } + break; + } + } + } + + JSHandle CreateJsonArray(JsonContinuation continuation, + std::vector> &elementsList) { - SkipStartWhiteSpace(); - Tokens token = ParseToken(); - switch (token) { - case Tokens::OBJECT: - return ParseObject(inObjorArr); - case Tokens::ARRAY: - return ParseArray(inObjorArr); - case Tokens::LITERAL_TRUE: - return ParseLiteralTrue(); - case Tokens::LITERAL_FALSE: - return ParseLiteralFalse(); - case Tokens::LITERAL_NULL: - return ParseLiteralNull(); - case Tokens::NUMBER: - return ParseNumber(inObjorArr); - case Tokens::STRING: - return ParseString(inObjorArr); - default: - THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON", JSTaggedValue::Exception()); + size_t start = continuation.index_; + size_t size = elementsList.size() - start; + JSHandle array = factory_->NewJSArray(size, initialJSArrayClass_); + JSHandle elements = factory_->NewJsonFixedArray(start, size, elementsList); + JSHandle obj(array); + obj->SetElements(thread_, elements); + return JSHandle(array); + } + + JSHandle CreateJsonObject(JsonContinuation continuation, + std::vector> &propertyList) + { + size_t start = continuation.index_; + size_t size = propertyList.size() - start; + JSHandle obj = factory_->NewJSObject(initialJSObjectClass_); + for (size_t i = 0; i < size; i += 2) { // 2: prop name and value + JSTaggedValue res = ObjectFastOperator::SetPropertyByValue(thread_, obj.GetTaggedValue(), + propertyList[start + i].GetTaggedValue(), propertyList[start + i + 1].GetTaggedValue()); + if (res.IsHole()) { + // slow path + JSTaggedValue::SetProperty(thread_, JSHandle(obj), propertyList[start + i], + propertyList[start + i + 1], true); + } } + return JSHandle(obj); } JSTaggedValue ParseNumber(bool inObjorArr = false) @@ -116,7 +301,8 @@ protected: int32_t fastInteger = 0; bool isNumber = ReadNumberRange(isFast, fastInteger); if (!isNumber) { - THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Number in JSON", JSTaggedValue::Exception()); + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Number in JSON Array Or Object", + JSTaggedValue::Exception()); } if (isFast) { return JSTaggedValue(fastInteger); @@ -143,7 +329,7 @@ protected: } std::string strNum(current, end_ + 1); - current_ = end_; + current_ = end_ + 1; errno = 0; // reset errno to 0 to avoid errno has been changed double v = std::strtod(strNum.c_str(), nullptr); if (errno == ERANGE) { @@ -221,6 +407,7 @@ protected: } } ASSERT(res.size() <= static_cast(UINT32_MAX)); + Advance(); return factory_->NewFromUtf8Literal(reinterpret_cast(res.c_str()), res.size()) .GetTaggedValue(); } @@ -229,111 +416,6 @@ protected: virtual JSTaggedValue ParseString(bool inObjorArr = false) = 0; - JSTaggedValue ParseArray(bool inObjorArr = false) - { - if (UNLIKELY(*range_ != ']' && !inObjorArr)) { - THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Array in JSON", JSTaggedValue::Exception()); - } - - Advance(); - SkipStartWhiteSpace(); - - if (*current_ == ']') { - JSHandle arr = factory_->NewJSArray(); - return arr.GetTaggedValue(); - } - - JSTaggedValue value; - std::vector> vec; - while (current_ <= range_) { - value = ParseJSONText(true); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); - vec.emplace_back(JSHandle(thread_, value)); - GetNextNonSpaceChar(); - if (*current_ == ',') { - Advance(); - } else if (*current_ == ']') { - if (inObjorArr || current_ == range_) { - return CreateJsonArray(vec); - } else { - THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Array in JSON", JSTaggedValue::Exception()); - } - } - } - THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Array in JSON", JSTaggedValue::Exception()); - } - - inline JSTaggedValue CreateJsonArray(std::vector> &vec) - { - JSHandle array = factory_->NewJSArray(); - size_t arrayLength = vec.size(); - array->SetArrayLength(thread_, arrayLength); - JSHandle newElements = factory_->NewTaggedArray(arrayLength); - for (size_t i = 0; i < arrayLength; i++) { - newElements->Set(thread_, i, vec[i]); - } - JSHandle obj(array); - obj->SetElements(thread_, newElements); - return array.GetTaggedValue(); - } - - JSTaggedValue ParseObject(bool inObjorArr = false) - { - if (UNLIKELY(*range_ != '}' && !inObjorArr)) { - THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Object in JSON", JSTaggedValue::Exception()); - } - - JSHandle proto(env_->GetObjectFunction()); - JSHandle result = factory_->NewJSObjectByConstructor(proto); - Advance(); - if (*current_ == '}') { - return result.GetTaggedValue(); - } - - JSMutableHandle keyHandle(thread_, JSTaggedValue::Undefined()); - JSTaggedValue value; - while (current_ <= range_) { - SkipStartWhiteSpace(); - if (*current_ == '"') { - keyHandle.Update(ParseString(true)); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); - } else { - if (*current_ == '}' && (inObjorArr || current_ == range_)) { - return result.GetTaggedValue(); - } - THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Object in JSON", JSTaggedValue::Exception()); - } - GetNextNonSpaceChar(); - if (*current_ == ':') { - Advance(); - } else { - THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Object in JSON", JSTaggedValue::Exception()); - } - value = ParseJSONText(true); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); - // fast path - JSTaggedValue res = ObjectFastOperator::SetPropertyByValue(thread_, result.GetTaggedValue(), - keyHandle.GetTaggedValue(), value); - if (res.IsHole()) { - // slow path - JSTaggedValue::SetProperty(thread_, JSHandle(result), keyHandle, - JSHandle(thread_, value), true); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); - } - GetNextNonSpaceChar(); - if (*current_ == ',') { - Advance(); - } else if (*current_ == '}') { - if (inObjorArr || current_ == range_) { - return result.GetTaggedValue(); - } else { - THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Object in JSON", JSTaggedValue::Exception()); - } - } - } - THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Object in JSON", JSTaggedValue::Exception()); - } - void SkipEndWhiteSpace() { while (current_ != end_) { @@ -401,7 +483,7 @@ protected: if (UNLIKELY(remainingLength < 3)) { // 3: literalTrue length - 1 THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON", JSTaggedValue::Exception()); } - bool isMatch = MatchText(literalTrue, 3); // 3: literalTrue length - 1 + bool isMatch = MatchText(literalTrue, 4); // 4: literalTrue length if (LIKELY(isMatch)) { return JSTaggedValue::True(); } @@ -415,7 +497,7 @@ protected: if (UNLIKELY(remainingLength < 4)) { // 4: literalFalse length - 1 THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON", JSTaggedValue::Exception()); } - bool isMatch = MatchText(literalFalse, 4); // 4: literalFalse length - 1 + bool isMatch = MatchText(literalFalse, 5); // 5: literalFalse length if (LIKELY(isMatch)) { return JSTaggedValue::False(); } @@ -429,7 +511,7 @@ protected: if (UNLIKELY(remainingLength < 3)) { // 3: literalNull length - 1 THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON", JSTaggedValue::Exception()); } - bool isMatch = MatchText(literalNull, 3); // 3: literalNull length - 1 + bool isMatch = MatchText(literalNull, 4); // 4: literalNull length if (LIKELY(isMatch)) { return JSTaggedValue::Null(); } @@ -439,7 +521,7 @@ protected: bool MatchText(const char *str, uint32_t matchLen) { // first char is already matched - for (uint32_t pos = 1; pos <= matchLen; ++pos) { + for (uint32_t pos = 1; pos < matchLen; ++pos) { if (current_[pos] != str[pos]) { return false; } @@ -472,7 +554,7 @@ protected: i = (i * 10) + ((*current) - '0'); } fastInteger = i * sign; - current_ = advance - 1; + current_ = advance; return true; } isFast = false; @@ -679,6 +761,8 @@ protected: JSThread *thread_ {nullptr}; ObjectFactory *factory_ {nullptr}; GlobalEnv *env_ {nullptr}; + JSHandle initialJSArrayClass_; + JSHandle initialJSObjectClass_; }; class Utf8JsonParser : public JsonParser { @@ -692,10 +776,11 @@ public: JSHandle Parse(EcmaString *str) { ASSERT(str != nullptr); - uint32_t len = EcmaStringAccessor(str).GetLength(); + auto stringAccessor = EcmaStringAccessor(str); + uint32_t len = stringAccessor.GetLength(); ASSERT(len != UINT32_MAX); CVector buf(len + 1); - EcmaStringAccessor(str).WriteToFlatUtf8(buf.data(), len); + stringAccessor.WriteToFlatUtf8(buf.data(), len); Text begin = buf.data(); return Launch(begin, begin + len); } @@ -720,7 +805,7 @@ private: ASSERT(strLength <= static_cast(UINT32_MAX)); JSTaggedValue res = factory_->NewCompressedUtf8( reinterpret_cast(current_), strLength).GetTaggedValue(); - current_ = end_; + current_ = end_ + 1; return res; } } else { @@ -733,6 +818,7 @@ private: } if (LIKELY(isFastString)) { std::string_view value(reinterpret_cast(current_), end_ - current_); + current_ = end_ + 1; ASSERT(value.size() <= static_cast(UINT32_MAX)); return factory_->NewFromUtf8LiteralCompress( reinterpret_cast(value.data()), value.size()).GetTaggedValue(); @@ -812,13 +898,13 @@ private: if (isFastString) { if (isAscii) { std::string value(current_, end_); // from uint16_t* to std::string, can't use std::string_view - current_ = end_; + current_ = end_ + 1; ASSERT(value.size() <= static_cast(UINT32_MAX)); return factory_->NewFromUtf8LiteralCompress( reinterpret_cast(value.c_str()), value.size()).GetTaggedValue(); } std::u16string_view value(reinterpret_cast(current_), end_ - current_); - current_ = end_; + current_ = end_ + 1; ASSERT(value.size() <= static_cast(UINT32_MAX)); return factory_->NewFromUtf16LiteralNotCompress( reinterpret_cast(value.data()), value.size()).GetTaggedValue(); @@ -835,11 +921,13 @@ private: if (isAscii) { std::string value(current_, end_); // from uint16_t* to std::string, can't use std::string_view ASSERT(value.size() <= static_cast(UINT32_MAX)); + current_ = end_ + 1; return factory_->NewFromUtf8LiteralCompress( reinterpret_cast(value.c_str()), value.size()).GetTaggedValue(); } std::u16string_view value(reinterpret_cast(current_), end_ - current_); ASSERT(value.size() <= static_cast(UINT32_MAX)); + current_ = end_ + 1; return factory_->NewFromUtf16LiteralNotCompress( reinterpret_cast(value.data()), value.size()).GetTaggedValue(); } diff --git a/ecmascript/base/tests/json_parser_test.cpp b/ecmascript/base/tests/json_parser_test.cpp index 6a0a03f8373e3b82b249d51448978589c05668da..c29fd60f6c6a9e3c6876395c9e804e4ba218e8b9 100644 --- a/ecmascript/base/tests/json_parser_test.cpp +++ b/ecmascript/base/tests/json_parser_test.cpp @@ -125,7 +125,7 @@ HWTEST_F_L0(JsonParserTest, Parser_003) JSHandle handleMsg(factory->NewFromASCII( "\t\r \n{\t\r \n \"json\"\t\r\n:\t\r \n{\t\r \n}\t\r \n,\t\r \n \"prop2\"\t\r \n:\t\r \n [\t\r \nfalse\t\r" - "\n,\t\r \nnull\t\r \ntrue\t\r,123.456\t\r \n]\t\r \n}\t\r \n")); + "\n,\t\r \nnull\t\r, \ntrue\t\r,123.456\t\r \n]\t\r \n}\t\r \n")); JSHandle handleStr(JSTaggedValue::ToString(thread, handleMsg)); // JSON Object JSHandle result = parser.Parse(*handleStr); EXPECT_TRUE(result->IsECMAObject()); diff --git a/ecmascript/builtins/builtins_ark_tools.cpp b/ecmascript/builtins/builtins_ark_tools.cpp index 509de41350b6eb0f054ef0f8eeb4171741410755..1bcdc1942fc0ace4e9a4d3b0a8f1dd795c1a1f00 100644 --- a/ecmascript/builtins/builtins_ark_tools.cpp +++ b/ecmascript/builtins/builtins_ark_tools.cpp @@ -25,6 +25,9 @@ #include "ecmascript/napi/include/dfx_jsnapi.h" #include "ecmascript/mem/clock_scope.h" #include "ecmascript/property_detector-inl.h" +#include "ecmascript/js_arraybuffer.h" +#include "ecmascript/interpreter/fast_runtime_stub-inl.h" +#include "builtins_typedarray.h" namespace panda::ecmascript::builtins { using StringHelper = base::StringHelper; @@ -470,4 +473,154 @@ JSTaggedValue BuiltinsArkTools::PGOAssertType([[maybe_unused]] EcmaRuntimeCallIn LOG_ECMA(INFO) << "Enter PGOAssertType"; return JSTaggedValue::Undefined(); } + +JSTaggedValue BuiltinsArkTools::ToLength([[maybe_unused]] EcmaRuntimeCallInfo *info) +{ + LOG_ECMA(INFO) << "Enter ToLength()"; + ASSERT(info); + JSThread *thread = info->GetThread(); + JSHandle key = GetCallArg(info, 0); + return JSTaggedValue::ToLength(thread, key); +} + +JSTaggedValue BuiltinsArkTools::HasHoleyElements([[maybe_unused]] EcmaRuntimeCallInfo *info) +{ + LOG_ECMA(INFO) << "Enter HasHoleyElements()"; + ASSERT(info); + JSThread *thread = info->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle array = GetCallArg(info, 0); + if (!array->IsJSArray()) { + return JSTaggedValue::False(); + } + JSHandle elements(thread, JSHandle::Cast(array)->GetElements()); + uint32_t len = JSHandle::Cast(array)->GetArrayLength(); + for (uint32_t i = 0; i < len; i++) { + if (elements->Get(i).IsHole()) { + return JSTaggedValue::True(); + } + } + return JSTaggedValue::False(); +} + +JSTaggedValue BuiltinsArkTools::HasDictionaryElements([[maybe_unused]] EcmaRuntimeCallInfo *info) +{ + LOG_ECMA(INFO) << "Enter HasDictionaryElements()"; + ASSERT(info); + JSThread *thread = info->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle objValue = GetCallArg(info, 0); + JSHandle obj = JSHandle::Cast(objValue); + return JSTaggedValue(obj->GetJSHClass()->IsDictionaryMode()); +} + +JSTaggedValue BuiltinsArkTools::HasSmiElements([[maybe_unused]] EcmaRuntimeCallInfo *info) +{ + LOG_ECMA(INFO) << "Enter HasSmiElements()"; + ASSERT(info); + JSThread *thread = info->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle array = GetCallArg(info, 0); + if (!array->IsJSArray()) { + return JSTaggedValue::False(); + } + JSHandle elements(thread, JSHandle::Cast(array)->GetElements()); + uint32_t len = JSHandle::Cast(array)->GetArrayLength(); + for (uint32_t i = 0; i < len; i++) { + if (elements->Get(i).IsInt()) { + return JSTaggedValue::True(); + } + } + return JSTaggedValue::False(); +} + +JSTaggedValue BuiltinsArkTools::HasDoubleElements([[maybe_unused]] EcmaRuntimeCallInfo *info) +{ + LOG_ECMA(INFO) << "Enter HasDoubleElements()"; + ASSERT(info); + JSThread *thread = info->GetThread(); + JSHandle array = GetCallArg(info, 0); + if (!array->IsJSArray()) { + return JSTaggedValue::False(); + } + JSHandle elements(thread, JSHandle::Cast(array)->GetElements()); + uint32_t len = JSHandle::Cast(array)->GetArrayLength(); + for (uint32_t i = 0; i < len; i++) { + if (elements->Get(i).IsDouble() && !elements->Get(i).IsZero()) { + return JSTaggedValue::True(); + } + } + return JSTaggedValue::False(); +} + +JSTaggedValue BuiltinsArkTools::HasObjectElements([[maybe_unused]] EcmaRuntimeCallInfo *info) +{ + LOG_ECMA(INFO) << "Enter HasObjectElements()"; + ASSERT(info); + JSThread *thread = info->GetThread(); + JSHandle array = GetCallArg(info, 0); + if (!array->IsJSArray()) { + return JSTaggedValue::False(); + } + JSHandle elements(thread, JSHandle::Cast(array)->GetElements()); + uint32_t len = JSHandle::Cast(array)->GetArrayLength(); + for (uint32_t i = 0; i < len; i++) { + if (elements->Get(i).IsObject()) { + return JSTaggedValue::True(); + } + } + return JSTaggedValue::False(); +} + +JSTaggedValue BuiltinsArkTools::ArrayBufferDetach([[maybe_unused]] EcmaRuntimeCallInfo *info) +{ + LOG_ECMA(INFO) << "Enter ArrayBufferDetach()"; + ASSERT(info); + JSThread *thread = info->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle obj1 = GetCallArg(info, 0); + JSHandle arrBuf = JSHandle::Cast(obj1); + arrBuf->Detach(thread); + return JSTaggedValue::Undefined(); +} + +JSTaggedValue BuiltinsArkTools::HaveSameMap([[maybe_unused]] EcmaRuntimeCallInfo *info) +{ + LOG_ECMA(INFO) << "Enter HaveSameMap()"; + JSThread *thread = info->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle obj1 = GetCallArg(info, 0); + JSHandle obj2 = GetCallArg(info, 1); + JSHClass *obj1Hclass = obj1->GetTaggedObject()->GetClass(); + JSHClass *obj2Hclass = obj2->GetTaggedObject()->GetClass(); + bool res = (obj1Hclass == obj2Hclass); + if (!res) { + return JSTaggedValue::False(); + } + JSHandle jsobj1(obj1); + JSHandle jsobj2(obj2); + JSHandle nameList1 = JSObject::EnumerableOwnNames(thread, jsobj1); + JSHandle nameList2 = JSObject::EnumerableOwnNames(thread, jsobj2); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + uint32_t len1 = nameList1->GetLength(); + uint32_t len2 = nameList2->GetLength(); + if (len1 != len2) { + return JSTaggedValue::False(); + } + for (uint32_t i = 0; i < len1; i++) { + if (obj1->IsJSArray()) { + JSTaggedValue objTagged1 = JSObject::GetProperty(thread, obj1, i).GetValue().GetTaggedValue(); + JSTaggedValue objTagged2 = JSObject::GetProperty(thread, obj2, i).GetValue().GetTaggedValue(); + if (FastRuntimeStub::FastTypeOf(thread, objTagged1) != + FastRuntimeStub::FastTypeOf(thread, objTagged2)) { + return JSTaggedValue::False(); + } + } else if (JSObject::GetProperty(thread, obj1, i).GetValue() != + JSObject::GetProperty(thread, obj2, i).GetValue()) { + return JSTaggedValue::False(); + } + } + return JSTaggedValue::True(); +} + } // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_ark_tools.h b/ecmascript/builtins/builtins_ark_tools.h index 105b57de2c7a59dbfb2df56524279de67f23d3e7..ef0fbc5411abe249416c8d9651839ae28af524dc 100644 --- a/ecmascript/builtins/builtins_ark_tools.h +++ b/ecmascript/builtins/builtins_ark_tools.h @@ -44,19 +44,27 @@ V("timeInUs", TimeInUs, 0, INVALID) \ V("isAOTCompiled", IsAOTCompiled, 1, INVALID) -#define BUILTIN_ARK_TOOLS_FUNCTIONS_REGRESS(V) \ - V("prepareFunctionForOptimization", PrepareFunctionForOptimization, 1, INVALID) \ - V("optimizeFunctionOnNextCall", OptimizeFunctionOnNextCall, 1, INVALID) \ - V("optimizeMaglevOnNextCall", OptimizeMaglevOnNextCall, 1, INVALID) \ - V("deoptimizeFunction", DeoptimizeFunction, 1, INVALID) \ - V("optimizeOsr", OptimizeOsr, 1, INVALID) \ - V("neverOptimizeFunction", NeverOptimizeFunction, 1, INVALID) \ - V("heapObjectVerify", HeapObjectVerify, 1, INVALID) \ - V("disableOptimizationFinalization", DisableOptimizationFinalization, 0, INVALID) \ - V("deoptimizeNow", DeoptimizeNow, 0, INVALID) \ - V("deoptimize_now", DeoptimizeNow, 0, INVALID) \ - V("waitForBackgroundOptimization", WaitForBackgroundOptimization, 0, INVALID) \ - V("gc", Gc, 0, INVALID) +#define BUILTIN_ARK_TOOLS_FUNCTIONS_REGRESS(V) \ + V("prepareFunctionForOptimization", PrepareFunctionForOptimization, 1, INVALID) \ + V("optimizeFunctionOnNextCall", OptimizeFunctionOnNextCall, 1, INVALID) \ + V("optimizeMaglevOnNextCall", OptimizeMaglevOnNextCall, 1, INVALID) \ + V("deoptimizeFunction", DeoptimizeFunction, 1, INVALID) \ + V("optimizeOsr", OptimizeOsr, 1, INVALID) \ + V("neverOptimizeFunction", NeverOptimizeFunction, 1, INVALID) \ + V("heapObjectVerify", HeapObjectVerify, 1, INVALID) \ + V("disableOptimizationFinalization", DisableOptimizationFinalization, 0, INVALID) \ + V("deoptimizeNow", DeoptimizeNow, 0, INVALID) \ + V("deoptimize_now", DeoptimizeNow, 0, INVALID) \ + V("waitForBackgroundOptimization", WaitForBackgroundOptimization, 0, INVALID) \ + V("gc", Gc, 0, INVALID) \ + V("toLength", ToLength, 1, INVALID) \ + V("hasHoleyElements", HasHoleyElements, 1, INVALID) \ + V("hasDictionaryElements", HasDictionaryElements, 1, INVALID) \ + V("hasSmiElements", HasSmiElements, 1, INVALID) \ + V("hasDoubleElements", HasDoubleElements, 1, INVALID) \ + V("hasObjectElements", HasObjectElements, 1, INVALID) \ + V("arrayBufferDetach", ArrayBufferDetach, 1, INVALID) \ + V("haveSameMap", HaveSameMap, 2, INVALID) #ifdef ECMASCRIPT_SUPPORT_CPUPROFILER #define BUILTIN_ARK_TOOLS_FUNCTIONS_CPUPROFILER(V) \ @@ -148,6 +156,22 @@ public: static JSTaggedValue PGOAssertType(EcmaRuntimeCallInfo *info); + static JSTaggedValue ToLength(EcmaRuntimeCallInfo *info); + + static JSTaggedValue HasHoleyElements(EcmaRuntimeCallInfo *info); + + static JSTaggedValue HasDictionaryElements(EcmaRuntimeCallInfo *info); + + static JSTaggedValue HasSmiElements(EcmaRuntimeCallInfo *info); + + static JSTaggedValue HasDoubleElements(EcmaRuntimeCallInfo *info); + + static JSTaggedValue HasObjectElements(EcmaRuntimeCallInfo *info); + + static JSTaggedValue ArrayBufferDetach(EcmaRuntimeCallInfo *info); + + static JSTaggedValue HaveSameMap(EcmaRuntimeCallInfo *info); + static Span GetArkToolsFunctions() { return Span(ARK_TOOLS_FUNCTIONS); diff --git a/ecmascript/builtins/builtins_array.cpp b/ecmascript/builtins/builtins_array.cpp index efe225bdf17d6d024e5db75012eff7baa619852b..df49e25577094267284005eacbdc0e9752088c9d 100644 --- a/ecmascript/builtins/builtins_array.cpp +++ b/ecmascript/builtins/builtins_array.cpp @@ -1515,10 +1515,11 @@ JSTaggedValue BuiltinsArray::Pop(EcmaRuntimeCallInfo *argv) // 1. Let O be ToObject(this value). JSHandle thisHandle = GetThis(argv); - if (thisHandle->IsStableJSArray(thread)) { + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + if (thisHandle->IsStableJSArray(thread) && JSObject::IsArrayLengthWritable(thread, thisObjHandle)) { return JSStableArray::Pop(JSHandle::Cast(thisHandle), argv); } - JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle thisObjVal(thisObjHandle); diff --git a/ecmascript/builtins/builtins_array.h b/ecmascript/builtins/builtins_array.h index 18cf335c2cc864e863499984448c90772ea65e1f..e3f7a463993207fa882640f14ed69c2ad7e622b0 100644 --- a/ecmascript/builtins/builtins_array.h +++ b/ecmascript/builtins/builtins_array.h @@ -49,9 +49,9 @@ /* Array.prototype.filter ( callbackfn [ , thisArg ] ) */ \ V("filter", Filter, 1, ArrayFilter) \ /* Array.prototype.find ( predicate [ , thisArg ] ) */ \ - V("find", Find, 1, INVALID) \ + V("find", Find, 1, ArrayFind) \ /* Array.prototype.findIndex ( predicate [ , thisArg ] ) */ \ - V("findIndex", FindIndex, 1, INVALID) \ + V("findIndex", FindIndex, 1, ArrayFindIndex) \ /* Array.prototype.findLast ( predicate [ , thisArg ] ) */ \ V("findLast", FindLast, 1, INVALID) \ /* Array.prototype.findLastIndex ( predicate [ , thisArg ] ) */ \ @@ -107,7 +107,7 @@ /* Array.prototype.unshift ( ...items ) */ \ V("unshift", Unshift, 1, INVALID) \ /* Array.prototype.values ( ) */ \ - V("values", Values, 0, INVALID) \ + V("values", Values, 0, ArrayValues) \ /* Array.prototype.with ( index, value ) */ \ V("with", With, 2, INVALID) diff --git a/ecmascript/compiler/aot_compiler.cpp b/ecmascript/compiler/aot_compiler.cpp index 0ad7e61e2df76572b89ced3142bab7cd9606f357..59dbd4c6695dd3ed203e7abb3d0d2643e6d491bb 100644 --- a/ecmascript/compiler/aot_compiler.cpp +++ b/ecmascript/compiler/aot_compiler.cpp @@ -152,7 +152,8 @@ int Main(const int argc, const char **argv) AOTFileGenerator generator(&log, &logList, vm, cOptions.triple_); const auto &fileInfos = cPreprocessor.GetAbcFileInfo(); CompileValidFiles(passManager, generator, ret, fileInfos); - generator.SaveAOTFile(cOptions.outputFileName_ + AOTFileManager::FILE_EXTENSION_AN); + std::string appSignature = cPreprocessor.GetMainPkgArgsAppSignature(); + generator.SaveAOTFile(cOptions.outputFileName_ + AOTFileManager::FILE_EXTENSION_AN, appSignature); generator.SaveSnapshotFile(); log.Print(); } diff --git a/ecmascript/compiler/aot_compiler_preprocessor.cpp b/ecmascript/compiler/aot_compiler_preprocessor.cpp index 79dce24409fcebdbd0716a8cddceff243eb9d413..868bbc1eb75db4a8ad60a7ea8e252767fb97fff6 100644 --- a/ecmascript/compiler/aot_compiler_preprocessor.cpp +++ b/ecmascript/compiler/aot_compiler_preprocessor.cpp @@ -253,4 +253,9 @@ void AotCompilerPreprocessor::SnapshotInitialize() PGOTypeManager *ptManager = vm_->GetJSThread()->GetCurrentEcmaContext()->GetPTManager(); ptManager->InitAOTSnapshot(fileInfos_.size()); } + +std::string AotCompilerPreprocessor::GetMainPkgArgsAppSignature() const +{ + return GetMainPkgArgs() == nullptr ? "" : GetMainPkgArgs()->GetAppSignature(); +} } // namespace panda::ecmascript::kungfu \ No newline at end of file diff --git a/ecmascript/compiler/aot_compiler_preprocessor.h b/ecmascript/compiler/aot_compiler_preprocessor.h index 85e3be70202f567fb8fde29528953d68900d031a..8a1db060e44e9f22389f7ed0edd3ab1f450289e0 100644 --- a/ecmascript/compiler/aot_compiler_preprocessor.h +++ b/ecmascript/compiler/aot_compiler_preprocessor.h @@ -95,6 +95,8 @@ public: void GeneratePGOTypes(const CompilationOptions &cOptions); void SnapshotInitialize(); + + std::string GetMainPkgArgsAppSignature() const; bool GetCompilerResult() { diff --git a/ecmascript/compiler/aot_file/aot_file_manager.h b/ecmascript/compiler/aot_file/aot_file_manager.h index f35534143285ad967fd929a08492656dfaf1f5f3..e2200788d34610db5fcc85bb2e80f1913aa09ab8 100644 --- a/ecmascript/compiler/aot_file/aot_file_manager.h +++ b/ecmascript/compiler/aot_file/aot_file_manager.h @@ -21,6 +21,7 @@ #include "ecmascript/base/file_header.h" #include "ecmascript/compiler/aot_file/an_file_info.h" #include "ecmascript/compiler/aot_file/aot_file_info.h" +#include "ecmascript/compiler/aot_snapshot/snapshot_constantpool_data.h" #include "ecmascript/compiler/aot_file/binary_buffer_parser.h" #include "ecmascript/compiler/aot_file/module_section_des.h" #include "ecmascript/compiler/aot_file/stub_file_info.h" @@ -37,23 +38,25 @@ class JSpandafile; class JSThread; /* AOTLiteralInfo - * +--------------------------------+---- - * | cache | ^ - * | ... | | - * | -1 (No AOT Function Entry) | | - * | AOT Function Entry Index | | - * | AOT Function Entry Index | | - * +--------------------------------+---- - * | AOT Instance Hclass (IHC) | - * | AOT Constructor Hclass (CHC) | - * +--------------------------------+ + * +-----------------------------------+---- + * | cache | ^ + * | ... | | + * | -1 (No AOT Function Entry) | | + * | AOT Function Entry Index | | + * | AOT Function Entry Index | | + * +-----------------------------------+---- + * | AOT Instance Hclass (IHC) | + * | AOT Constructor Hclass (CHC) | + * | AOT ElementIndex (ElementIndex) | + * +-----------------------------------+ */ class AOTLiteralInfo : public TaggedArray { public: static constexpr size_t NO_FUNC_ENTRY_VALUE = -1; static constexpr size_t AOT_CHC_INDEX = 1; static constexpr size_t AOT_IHC_INDEX = 2; - static constexpr size_t RESERVED_LENGTH = AOT_IHC_INDEX; + static constexpr size_t AOT_ELEMENT_INDEX = 3; + static constexpr size_t RESERVED_LENGTH = AOT_ELEMENT_INDEX; static AOTLiteralInfo *Cast(TaggedObject *object) { @@ -71,6 +74,7 @@ public: TaggedArray::InitializeWithSpecialValue(initValue, capacity + RESERVED_LENGTH, extraLength); SetIhc(JSTaggedValue::Undefined()); SetChc(JSTaggedValue::Undefined()); + SetElementIndex(JSTaggedValue(kungfu::BaseSnapshotInfo::AOT_ELEMENT_INDEX_DEFAULT_VALUE)); } inline uint32_t GetCacheLength() const @@ -98,6 +102,16 @@ public: return JSTaggedValue(Barriers::GetValue(GetData(), GetChcOffset())); } + inline void SetElementIndex(JSTaggedValue value) + { + Barriers::SetPrimitive(GetData(), GetElementIndexOffset(), value.GetRawData()); + } + + inline int GetElementIndex() const + { + return JSTaggedValue(Barriers::GetValue(GetData(), GetElementIndexOffset())).GetInt(); + } + inline void SetObjectToCache(JSThread *thread, uint32_t index, JSTaggedValue value) { Set(thread, index, value); @@ -118,6 +132,11 @@ private: { return JSTaggedValue::TaggedTypeSize() * (GetLength() - AOT_CHC_INDEX); } + + inline size_t GetElementIndexOffset() const + { + return JSTaggedValue::TaggedTypeSize() * (GetLength() - AOT_ELEMENT_INDEX); + } }; class AOTFileManager { diff --git a/ecmascript/compiler/aot_snapshot/snapshot_constantpool_data.cpp b/ecmascript/compiler/aot_snapshot/snapshot_constantpool_data.cpp index 3d2d4c12b68473fc1348ec1f705f4cff399e17f8..da1ea881a1122a90644baeb64474e7f915a9e09a 100644 --- a/ecmascript/compiler/aot_snapshot/snapshot_constantpool_data.cpp +++ b/ecmascript/compiler/aot_snapshot/snapshot_constantpool_data.cpp @@ -39,7 +39,8 @@ void BaseSnapshotInfo::Record(ItemData &data) void BaseSnapshotInfo::CollectLiteralInfo(EcmaVM *vm, JSHandle array, uint32_t constantPoolIndex, JSHandle snapshotConstantPool, const std::set &skippedMethods, - JSHandle ihc, JSHandle chc) + JSHandle ihc, JSHandle chc, + int32_t elementIndex) { JSThread *thread = vm->GetJSThread(); ObjectFactory *factory = vm->GetFactory(); @@ -73,6 +74,10 @@ void BaseSnapshotInfo::CollectLiteralInfo(EcmaVM *vm, JSHandle arra aotLiteralInfo->SetChc(chc.GetTaggedValue()); } + if (elementIndex != AOT_ELEMENT_INDEX_DEFAULT_VALUE) { + aotLiteralInfo->SetElementIndex(JSTaggedValue(elementIndex)); + } + snapshotConstantPool->SetObjectToCache(thread, constantPoolIndex, aotLiteralInfo.GetTaggedValue()); } @@ -197,6 +202,7 @@ void ArrayLiteralSnapshotInfo::StoreDataToGlobalData(EcmaVM *vm, const JSPandaFi const std::set &skippedMethods) { JSThread *thread = vm->GetJSThread(); + PGOTypeManager *ptManager = thread->GetCurrentEcmaContext()->GetPTManager(); for (auto item : info_) { const ItemData &data = item.second; JSHandle cp(thread, @@ -204,13 +210,14 @@ void ArrayLiteralSnapshotInfo::StoreDataToGlobalData(EcmaVM *vm, const JSPandaFi panda_file::File::EntityId id = cp->GetEntityId(data.constantPoolIdx_); JSHandle literal = LiteralDataExtractor::GetDatasIgnoreType( thread, jsPandaFile, id, cp, data.recordName_); + int32_t elementIndex = ptManager->GetElementsIndexByEntityId(id); uint32_t snapshotCpArrIdx = globalData.GetCpArrIdxByConstanPoolId(data.constantPoolId_); JSHandle snapshotCpArr(thread, globalData.GetCurSnapshotCpArray()); JSHandle snapshotCp(thread, snapshotCpArr->Get(snapshotCpArrIdx)); JSHandle ihc = thread->GlobalConstants()->GetHandledUndefined(); JSHandle chc = thread->GlobalConstants()->GetHandledUndefined(); - CollectLiteralInfo(vm, literal, data.constantPoolIdx_, snapshotCp, skippedMethods, ihc, chc); + CollectLiteralInfo(vm, literal, data.constantPoolIdx_, snapshotCp, skippedMethods, ihc, chc, elementIndex); globalData.RecordReviseData( ReviseData::ItemData {globalData.GetCurDataIdx(), snapshotCpArrIdx, data.constantPoolIdx_}); } diff --git a/ecmascript/compiler/aot_snapshot/snapshot_constantpool_data.h b/ecmascript/compiler/aot_snapshot/snapshot_constantpool_data.h index 67b46a5d28c06c9966d385bbc284342e4a87813c..c756e9b040502a60c8cd35f879ced3b9be0acc39 100644 --- a/ecmascript/compiler/aot_snapshot/snapshot_constantpool_data.h +++ b/ecmascript/compiler/aot_snapshot/snapshot_constantpool_data.h @@ -29,6 +29,8 @@ namespace panda::ecmascript::kungfu { class BaseSnapshotInfo { public: + static constexpr int32_t AOT_ELEMENT_INDEX_DEFAULT_VALUE = -1; + struct ItemData { CString recordName_; int32_t constantPoolId_ {0}; @@ -54,7 +56,8 @@ protected: void CollectLiteralInfo(EcmaVM *vm, JSHandle array, uint32_t constantPoolIndex, JSHandle snapshotConstantPool, const std::set &skippedMethods, - JSHandle ihc, JSHandle chc); + JSHandle ihc, JSHandle chc, + int32_t elementIndex = AOT_ELEMENT_INDEX_DEFAULT_VALUE); CUnorderedMap info_; }; diff --git a/ecmascript/compiler/builtins/builtins_array_stub_builder.cpp b/ecmascript/compiler/builtins/builtins_array_stub_builder.cpp index 13a7e4335c49200bd3457933be6500e0f44c248a..724a30b4f8f35c606c6837ff936ca1010d64d7dc 100644 --- a/ecmascript/compiler/builtins/builtins_array_stub_builder.cpp +++ b/ecmascript/compiler/builtins/builtins_array_stub_builder.cpp @@ -20,39 +20,125 @@ #include "ecmascript/compiler/profiler_operation.h" #include "ecmascript/compiler/rt_call_signature.h" #include "ecmascript/runtime_call_id.h" +#include "ecmascript/js_iterator.h" +#include "ecmascript/compiler/access_object_stub_builder.h" namespace panda::ecmascript::kungfu { void BuiltinsArrayStubBuilder::Concat(GateRef glue, GateRef thisValue, GateRef numArgs, Variable *result, Label *exit, Label *slowPath) { auto env = GetEnvironment(); - Label thisIsEmpty(env); - // Fast path if all the conditions below are satisfied: - // (1) this is an empty array with constructor not reset (see ArraySpeciesCreate for details); - // (2) At most one argument; - // (3) all the arguments (if exists) are empty arrays. - JsArrayRequirements reqThisValue; - reqThisValue.defaultConstructor = true; - Branch(IsJsArrayWithLengthLimit(glue, thisValue, MAX_LENGTH_ZERO, reqThisValue), &thisIsEmpty, slowPath); - Bind(&thisIsEmpty); + Label isHeapObject(env); + Branch(TaggedIsHeapObject(thisValue), &isHeapObject, slowPath); + Bind(&isHeapObject); { - Label atMostOneArg(env); - Label argValIsEmpty(env); - GateRef numArgsAsInt32 = TruncPtrToInt32(numArgs); - Branch(Int32LessThanOrEqual(numArgsAsInt32, Int32(1)), &atMostOneArg, slowPath); - Bind(&atMostOneArg); + Label isExtensible(env); + Branch(HasConstructor(thisValue), slowPath, &isExtensible); + Bind(&isExtensible); { - Label exactlyOneArg(env); - Branch(Int32Equal(numArgsAsInt32, Int32(0)), &argValIsEmpty, &exactlyOneArg); - Bind(&exactlyOneArg); - GateRef argVal = GetCallArg0(numArgs); - JsArrayRequirements reqArgVal; - Branch(IsJsArrayWithLengthLimit(glue, argVal, MAX_LENGTH_ZERO, reqArgVal), &argValIsEmpty, slowPath); - // Creates an empty array on fast path - Bind(&argValIsEmpty); - NewObjectStubBuilder newBuilder(this); - result->WriteVariable(newBuilder.CreateEmptyArray(glue)); - Jump(exit); + Label numArgsOne(env); + Branch(Int64Equal(numArgs, IntPtr(1)), &numArgsOne, slowPath); + Bind(&numArgsOne); + { + GateRef arg0 = GetCallArg0(numArgs); + Label allEcmaObject(env); + Label allStableJsArray(env); + GateRef isThisEcmaObject = IsEcmaObject(thisValue); + GateRef isArgEcmaObject = IsEcmaObject(arg0); + Branch(BoolAnd(isThisEcmaObject, isArgEcmaObject), &allEcmaObject, slowPath); + Bind(&allEcmaObject); + { + GateRef isThisStableJSArray = IsStableJSArray(glue, thisValue); + GateRef isArgStableJSArray = IsStableJSArray(glue, arg0); + Branch(BoolAnd(isThisStableJSArray, isArgStableJSArray), &allStableJsArray, slowPath); + Bind(&allStableJsArray); + { + GateRef maxArrayIndex = Int64(TaggedArray::MAX_ARRAY_INDEX); + GateRef thisLen = ZExtInt32ToInt64(GetArrayLength(thisValue)); + GateRef argLen = ZExtInt32ToInt64(GetArrayLength(arg0)); + GateRef sumArrayLen = Int64Add(argLen, thisLen); + Label notOverFlow(env); + Branch(Int64GreaterThan(sumArrayLen, maxArrayIndex), slowPath, ¬OverFlow); + Bind(¬OverFlow); + { + Label spreadable(env); + GateRef isSpreadable = IsConcatSpreadable(glue, thisValue); + GateRef argisSpreadable = IsConcatSpreadable(glue, arg0); + Branch(BoolAnd(isSpreadable, argisSpreadable), &spreadable, slowPath); + Bind(&spreadable); + { + Label setProperties(env); + GateRef glueGlobalEnvOffset = + IntPtr(JSThread::GlueData::GetGlueGlobalEnvOffset(env->Is32Bit())); + GateRef glueGlobalEnv = Load(VariableType::NATIVE_POINTER(), glue, glueGlobalEnvOffset); + auto arrayFunc = GetGlobalEnvValue(VariableType::JS_ANY(), glueGlobalEnv, + GlobalEnv::ARRAY_FUNCTION_INDEX); + GateRef intialHClass = Load(VariableType::JS_ANY(), arrayFunc, + IntPtr(JSFunction::PROTO_OR_DYNCLASS_OFFSET)); + NewObjectStubBuilder newBuilder(this); + newBuilder.SetParameters(glue, 0); + GateRef newArray = newBuilder.NewJSArrayWithSize(intialHClass, sumArrayLen); + Branch(TaggedIsException(newArray), exit, &setProperties); + Bind(&setProperties); + { + GateRef lengthOffset = IntPtr(JSArray::LENGTH_OFFSET); + Store(VariableType::INT32(), glue, newArray, lengthOffset, + TruncInt64ToInt32(sumArrayLen)); + GateRef accessor = GetGlobalConstantValue(VariableType::JS_ANY(), glue, + ConstantIndex::ARRAY_LENGTH_ACCESSOR); + SetPropertyInlinedProps(glue, newArray, intialHClass, accessor, + Int32(JSArray::LENGTH_INLINE_PROPERTY_INDEX)); + SetExtensibleToBitfield(glue, newArray, true); + GateRef thisEles = GetElementsArray(thisValue); + GateRef argEles = GetElementsArray(arg0); + GateRef newArrayEles = GetElementsArray(newArray); + DEFVARIABLE(i, VariableType::INT64(), Int64(0)); + DEFVARIABLE(j, VariableType::INT64(), Int64(0)); + DEFVARIABLE(k, VariableType::INT64(), Int64(0)); + Label loopHead(env); + Label loopEnd(env); + Label next(env); + Label loopExit(env); + Jump(&loopHead); + LoopBegin(&loopHead); + { + Branch(Int64LessThan(*i, thisLen), &next, &loopExit); + Bind(&next); + GateRef ele = GetValueFromTaggedArray(thisEles, *i); + SetValueToTaggedArray(VariableType::JS_ANY(), glue, newArrayEles, *j, ele); + Jump(&loopEnd); + } + Bind(&loopEnd); + i = Int64Add(*i, Int64(1)); + j = Int64Add(*j, Int64(1)); + LoopEnd(&loopHead); + Bind(&loopExit); + Label loopHead1(env); + Label loopEnd1(env); + Label next1(env); + Label loopExit1(env); + Jump(&loopHead1); + LoopBegin(&loopHead1); + { + Branch(Int64LessThan(*k, argLen), &next1, &loopExit1); + Bind(&next1); + GateRef ele = GetValueFromTaggedArray(argEles, *k); + SetValueToTaggedArray(VariableType::JS_ANY(), glue, newArrayEles, *j, ele); + Jump(&loopEnd1); + } + Bind(&loopEnd1); + k = Int64Add(*k, Int64(1)); + j = Int64Add(*j, Int64(1)); + LoopEnd(&loopHead1); + Bind(&loopExit1); + result->WriteVariable(newArray); + Jump(exit); + } + } + } + } + } + } } } } @@ -147,13 +233,20 @@ void BuiltinsArrayStubBuilder::Slice(GateRef glue, GateRef thisValue, GateRef nu Variable *result, Label *exit, Label *slowPath) { auto env = GetEnvironment(); + Label isHeapObject(env); + Branch(TaggedIsHeapObject(thisValue), &isHeapObject, slowPath); + Bind(&isHeapObject); + Label noConstructor(env); + Branch(HasConstructor(thisValue), slowPath, &noConstructor); + Bind(&noConstructor); Label thisIsEmpty(env); + Label thisNotEmpty(env); // Fast path if: // (1) this is an empty array with constructor not reset (see ArraySpeciesCreate for details); // (2) no arguments exist JsArrayRequirements req; req.defaultConstructor = true; - Branch(IsJsArrayWithLengthLimit(glue, thisValue, MAX_LENGTH_ZERO, req), &thisIsEmpty, slowPath); + Branch(IsJsArrayWithLengthLimit(glue, thisValue, MAX_LENGTH_ZERO, req), &thisIsEmpty, &thisNotEmpty); Bind(&thisIsEmpty); { Label noArgs(env); @@ -165,20 +258,248 @@ void BuiltinsArrayStubBuilder::Slice(GateRef glue, GateRef thisValue, GateRef nu result->WriteVariable(newBuilder.CreateEmptyArray(glue)); Jump(exit); } + Bind(&thisNotEmpty); + { + Label stableJSArray(env); + Label arrayLenNotZero(env); + + GateRef isThisStableJSArray = IsStableJSArray(glue, thisValue); + Branch(isThisStableJSArray, &stableJSArray, slowPath); + Bind(&stableJSArray); + + GateRef msg0 = GetCallArg0(numArgs); + GateRef msg1 = GetCallArg1(numArgs); + + GateRef thisArrLen = ZExtInt32ToInt64(GetArrayLength(thisValue)); + Label msg0Int(env); + Branch(TaggedIsInt(msg0), &msg0Int, slowPath); + Bind(&msg0Int); + DEFVARIABLE(start, VariableType::INT64(), Int64(0)); + DEFVARIABLE(end, VariableType::INT64(), thisArrLen); + + GateRef argStart = SExtInt32ToInt64(TaggedGetInt(msg0)); + Label arg0LessZero(env); + Label arg0NotLessZero(env); + Label startDone(env); + Branch(Int64LessThan(argStart, Int64(0)), &arg0LessZero, &arg0NotLessZero); + Bind(&arg0LessZero); + { + Label tempGreaterZero(env); + Label tempNotGreaterZero(env); + GateRef tempStart = Int64Add(argStart, thisArrLen); + Branch(Int64GreaterThan(tempStart, Int64(0)), &tempGreaterZero, &tempNotGreaterZero); + Bind(&tempGreaterZero); + { + start = tempStart; + Jump(&startDone); + } + Bind(&tempNotGreaterZero); + { + Jump(&startDone); + } + } + Bind(&arg0NotLessZero); + { + Label argLessLen(env); + Label argNotLessLen(env); + Branch(Int64LessThan(argStart, thisArrLen), &argLessLen, &argNotLessLen); + Bind(&argLessLen); + { + start = argStart; + Jump(&startDone); + } + Bind(&argNotLessLen); + { + start = thisArrLen; + Jump(&startDone); + } + } + Bind(&startDone); + Label endDone(env); + Label msg1Def(env); + Branch(TaggedIsUndefined(msg1), &endDone, &msg1Def); + Bind(&msg1Def); + { + Label msg1Int(env); + Branch(TaggedIsInt(msg1), &msg1Int, slowPath); + Bind(&msg1Int); + { + GateRef argEnd = SExtInt32ToInt64(TaggedGetInt(msg1)); + Label arg1LessZero(env); + Label arg1NotLessZero(env); + Branch(Int64LessThan(argEnd, Int64(0)), &arg1LessZero, &arg1NotLessZero); + Bind(&arg1LessZero); + { + Label tempGreaterZero(env); + Label tempNotGreaterZero(env); + GateRef tempEnd = Int64Add(argEnd, thisArrLen); + Branch(Int64GreaterThan(tempEnd, Int64(0)), &tempGreaterZero, &tempNotGreaterZero); + Bind(&tempGreaterZero); + { + end = tempEnd; + Jump(&endDone); + } + Bind(&tempNotGreaterZero); + { + end = Int64(0); + Jump(&endDone); + } + } + Bind(&arg1NotLessZero); + { + Label argLessLen(env); + Label argNotLessLen(env); + Branch(Int64LessThan(argEnd, thisArrLen), &argLessLen, &argNotLessLen); + Bind(&argLessLen); + { + end = argEnd; + Jump(&endDone); + } + Bind(&argNotLessLen); + { + end = thisArrLen; + Jump(&endDone); + } + } + } + } + + Bind(&endDone); + DEFVARIABLE(count, VariableType::INT64(), Int64(0)); + GateRef tempCnt = Int64Sub(*end, *start); + Label tempCntGreaterOrEqualZero(env); + Label tempCntDone(env); + Branch(Int64LessThan(tempCnt, Int64(0)), &tempCntDone, &tempCntGreaterOrEqualZero); + Bind(&tempCntGreaterOrEqualZero); + { + count = tempCnt; + Jump(&tempCntDone); + } + Bind(&tempCntDone); + GateRef newArray = NewArray(glue, *count); + GateRef thisEles = GetElementsArray(thisValue); + GateRef thisElesLen = ZExtInt32ToInt64(GetLengthOfTaggedArray(thisEles)); + GateRef newArrayEles = GetElementsArray(newArray); + + Label inThisEles(env); + Label outThisEles(env); + Branch(Int64GreaterThan(thisElesLen, Int64Add(*start, *count)), &inThisEles, &outThisEles); + Bind(&inThisEles); + { + DEFVARIABLE(idx, VariableType::INT64(), Int64(0)); + Label loopHead(env); + Label loopEnd(env); + Label next(env); + Label loopExit(env); + Jump(&loopHead); + LoopBegin(&loopHead); + { + Branch(Int64LessThan(*idx, *count), &next, &loopExit); + Bind(&next); + + GateRef ele = GetValueFromTaggedArray(thisEles, Int64Add(*idx, *start)); + SetValueToTaggedArray(VariableType::JS_ANY(), glue, newArrayEles, *idx, ele); + Jump(&loopEnd); + } + Bind(&loopEnd); + idx = Int64Add(*idx, Int64(1)); + LoopEnd(&loopHead); + Bind(&loopExit); + result->WriteVariable(newArray); + Jump(exit); + } + Bind(&outThisEles); + { + DEFVARIABLE(idx, VariableType::INT64(), Int64(0)); + Label loopHead(env); + Label loopEnd(env); + Label next(env); + Label loopExit(env); + Jump(&loopHead); + LoopBegin(&loopHead); + { + Branch(Int64LessThan(*idx, *count), &next, &loopExit); + Bind(&next); + GateRef index = Int64Add(*idx, *start); + DEFVARIABLE(ele, VariableType::JS_ANY(), Hole()); + + Label indexOutRange(env); + Label indexInRange(env); + Label setEle(env); + Branch(Int64GreaterThan(thisElesLen, index), &indexInRange, &indexOutRange); + Bind(&indexInRange); + { + ele = GetValueFromTaggedArray(thisEles, index); + Jump(&setEle); + } + Bind(&indexOutRange); + { + ele = Hole(); + Jump(&setEle); + } + Bind(&setEle); + SetValueToTaggedArray(VariableType::JS_ANY(), glue, newArrayEles, *idx, *ele); + Jump(&loopEnd); + } + + Bind(&loopEnd); + idx = Int64Add(*idx, Int64(1)); + LoopEnd(&loopHead); + Bind(&loopExit); + result->WriteVariable(newArray); + Jump(exit); + } + } } // Note: unused arguments are reserved for further development -void BuiltinsArrayStubBuilder::Reverse([[maybe_unused]] GateRef glue, GateRef thisValue, - [[maybe_unused]] GateRef numArgs, +void BuiltinsArrayStubBuilder::Reverse(GateRef glue, GateRef thisValue, [[maybe_unused]] GateRef numArgs, Variable *result, Label *exit, Label *slowPath) { auto env = GetEnvironment(); - Label thisIsEmpty(env); - // Fast path is this is an array of length 0 or 1 - JsArrayRequirements req; - Branch(IsJsArrayWithLengthLimit(glue, thisValue, MAX_LENGTH_ONE, req), &thisIsEmpty, slowPath); - Bind(&thisIsEmpty); - // Returns thisValue on fast path + Label isHeapObject(env); + Branch(TaggedIsHeapObject(thisValue), &isHeapObject, slowPath); + Bind(&isHeapObject); + Label isJSArray(env); + GateRef jsCOWArray = IsJsCOWArray(thisValue); + GateRef jsJSArray = IsJsArray(thisValue); + Branch(BoolAnd(jsJSArray, BoolNot(jsCOWArray)), &isJSArray, slowPath); + Bind(&isJSArray); + Label stableJSArray(env); + GateRef isThisStableJSArray = IsStableJSArray(glue, thisValue); + Branch(isThisStableJSArray, &stableJSArray, slowPath); + Bind(&stableJSArray); + + GateRef thisArrLen = ZExtInt32ToInt64(GetArrayLength(thisValue)); + GateRef elements = GetElementsArray(thisValue); + DEFVARIABLE(i, VariableType::INT64(), Int64(0)); + DEFVARIABLE(j, VariableType::INT64(), Int64Sub(thisArrLen, Int64(1))); + + Label loopHead(env); + Label loopEnd(env); + Label next(env); + Label loopExit(env); + Jump(&loopHead); + LoopBegin(&loopHead); + { + Label arrayValue(env); + Label valueEqual(env); + Branch(Int64LessThan(*i, *j), &next, &loopExit); + Bind(&next); + { + GateRef lower = GetValueFromTaggedArray(elements, *i); + GateRef upper = GetValueFromTaggedArray(elements, *j); + + SetValueToTaggedArray(VariableType::JS_ANY(), glue, elements, *i, upper); + SetValueToTaggedArray(VariableType::JS_ANY(), glue, elements, *j, lower); + Jump(&loopEnd); + } + } + Bind(&loopEnd); + i = Int64Add(*i, Int64(1)); + j = Int64Sub(*j, Int64(1)); + LoopEnd(&loopHead); + Bind(&loopExit); result->WriteVariable(thisValue); Jump(exit); } @@ -221,6 +542,226 @@ GateRef BuiltinsArrayStubBuilder::IsJsArrayWithLengthLimit(GateRef glue, GateRef return ret; } +void BuiltinsArrayStubBuilder::Values(GateRef glue, GateRef thisValue, + [[maybe_unused]] GateRef numArgs, Variable *result, Label *exit, Label *slowPath) +{ + auto env = GetEnvironment(); + Label isHeapObject(env); + Branch(TaggedIsHeapObject(thisValue), &isHeapObject, slowPath); + Bind(&isHeapObject); + ConstantIndex iterClassIdx = ConstantIndex::JS_ARRAY_ITERATOR_CLASS_INDEX; + GateRef iteratorHClass = GetGlobalConstantValue(VariableType::JS_POINTER(), glue, iterClassIdx); + NewObjectStubBuilder newBuilder(this); + newBuilder.SetParameters(glue, 0); + GateRef glueGlobalEnvOffset = IntPtr(JSThread::GlueData::GetGlueGlobalEnvOffset(env->Is32Bit())); + GateRef glueGlobalEnv = Load(VariableType::NATIVE_POINTER(), glue, glueGlobalEnvOffset); + GateRef prototype = GetGlobalEnvValue(VariableType::JS_POINTER(), glueGlobalEnv, + GlobalEnv::ARRAY_ITERATOR_PROTOTYPE_INDEX); + SetPrototypeToHClass(VariableType::JS_POINTER(), glue, iteratorHClass, prototype); + GateRef iter = newBuilder.NewJSObject(glue, iteratorHClass); + SetIteratedArrayOfArrayIterator(glue, iter, thisValue); + SetNextIndexOfArrayIterator(glue, iter, Int32(0)); + GateRef kind = Int32(static_cast(IterationKind::VALUE)); + SetBitFieldOfArrayIterator(glue, iter, kind); + result->WriteVariable(iter); + Jump(exit); +} + +void BuiltinsArrayStubBuilder::Find(GateRef glue, GateRef thisValue, GateRef numArgs, + Variable *result, Label *exit, Label *slowPath) +{ + auto env = GetEnvironment(); + Label isHeapObject(env); + Branch(TaggedIsHeapObject(thisValue), &isHeapObject, slowPath); + Bind(&isHeapObject); + Label stableJSArray(env); + GateRef isThisStableJSArray = IsStableJSArray(glue, thisValue); + Branch(isThisStableJSArray, &stableJSArray, slowPath); + Bind(&stableJSArray); + + GateRef callbackFnHandle = GetCallArg0(numArgs); + Label arg0HeapObject(env); + Branch(TaggedIsHeapObject(callbackFnHandle), &arg0HeapObject, slowPath); + Bind(&arg0HeapObject); + Label callable(env); + Branch(IsCallable(callbackFnHandle), &callable, slowPath); + Bind(&callable); + GateRef argHandle = GetCallArg1(numArgs); + GateRef thisArrLen = ZExtInt32ToInt64(GetArrayLength(thisValue)); + + DEFVARIABLE(i, VariableType::INT64(), Int64(0)); + Label loopHead(env); + Label loopEnd(env); + Label next(env); + Label loopExit(env); + Jump(&loopHead); + LoopBegin(&loopHead); + { + Branch(Int64LessThan(*i, thisArrLen), &next, &loopExit); + Bind(&next); + GateRef kValue = FastGetPropertyByIndex(glue, thisValue, TruncInt64ToInt32(*i), ProfileOperation()); + GateRef key = Int64ToTaggedInt(*i); + Label hasException(env); + Label notHasException(env); + GateRef retValue = JSCallDispatch(glue, callbackFnHandle, Int32(NUM_MANDATORY_JSFUNC_ARGS), 0, + Circuit::NullGate(), JSCallMode::CALL_THIS_ARG3_WITH_RETURN, { argHandle, kValue, key, thisValue }); + Branch(HasPendingException(glue), &hasException, ¬HasException); + Bind(&hasException); + { + result->WriteVariable(retValue); + Jump(exit); + } + Bind(¬HasException); + { + Label find(env); + Branch(TaggedIsTrue(FastToBoolean(retValue)), &find, &loopEnd); + Bind(&find); + { + result->WriteVariable(kValue); + Jump(exit); + } + } + } + Bind(&loopEnd); + i = Int64Add(*i, Int64(1)); + LoopEnd(&loopHead); + Bind(&loopExit); + Jump(exit); +} + + +void BuiltinsArrayStubBuilder::FindIndex(GateRef glue, GateRef thisValue, GateRef numArgs, + Variable *result, Label *exit, Label *slowPath) +{ + auto env = GetEnvironment(); + Label isHeapObject(env); + Branch(TaggedIsHeapObject(thisValue), &isHeapObject, slowPath); + Bind(&isHeapObject); + Label stableJSArray(env); + Label notStableJSArray(env); + GateRef callbackFnHandle = GetCallArg0(numArgs); + Label arg0HeapObject(env); + Branch(TaggedIsHeapObject(callbackFnHandle), &arg0HeapObject, slowPath); + Bind(&arg0HeapObject); + Label callable(env); + Branch(IsCallable(callbackFnHandle), &callable, slowPath); + Bind(&callable); + result->WriteVariable(IntToTaggedPtr(Int32(-1))); + GateRef argHandle = GetCallArg1(numArgs); + GateRef thisArrLen = ZExtInt32ToInt64(GetArrayLength(thisValue)); + GateRef isThisStableJSArray = IsStableJSArray(glue, thisValue); + Branch(isThisStableJSArray, &stableJSArray, ¬StableJSArray); + Bind(&stableJSArray); + { + DEFVARIABLE(i, VariableType::INT64(), Int64(0)); + DEFVARIABLE(kValue, VariableType::JS_ANY(), Undefined()); + Label loopHead(env); + Label loopEnd(env); + Label next(env); + Label loopExit(env); + Jump(&loopHead); + LoopBegin(&loopHead); + { + Branch(Int64LessThan(*i, thisArrLen), &next, &loopExit); + Bind(&next); + DebugPrint(glue, {thisArrLen}); + GateRef thisEles = GetElementsArray(thisValue); + kValue = GetValueFromTaggedArray(thisEles, *i); + Label isHole(env); + Label notHole(env); + Branch(TaggedIsHole(*kValue), &isHole, ¬Hole); + Bind(&isHole); + { + GateRef res = FastGetPropertyByIndex(glue, thisValue, TruncInt64ToInt32(*i), ProfileOperation()); + Label resIsHole(env); + Label resNotHole(env); + Branch(TaggedIsHole(res), &resIsHole, &resNotHole); + Bind(&resIsHole); + kValue = Undefined(); + Jump(¬Hole); + Bind(&resNotHole); + kValue = res; + Jump(¬Hole); + } + Bind(¬Hole); + GateRef key = IntToTaggedPtr(*i); + Label hasException(env); + Label notHasException(env); + Label checkStable(env); + GateRef retValue = JSCallDispatch(glue, callbackFnHandle, Int32(NUM_MANDATORY_JSFUNC_ARGS), 0, + Circuit::NullGate(), JSCallMode::CALL_THIS_ARG3_WITH_RETURN, { argHandle, *kValue, key, thisValue }); + Branch(TaggedIsException(retValue), &hasException, ¬HasException); + Bind(&hasException); + { + result->WriteVariable(retValue); + Jump(exit); + } + Bind(¬HasException); + { + Label find(env); + Branch(TaggedIsTrue(FastToBoolean(retValue)), &find, &checkStable); + Bind(&find); + { + result->WriteVariable(key); + Jump(exit); + } + } + + Bind(&checkStable); + i = Int64Add(*i, Int64(1)); + Branch(IsStableJSArray(glue, thisValue), &loopEnd, ¬StableJSArray); + } + Bind(&loopEnd); + LoopEnd(&loopHead); + Bind(&loopExit); + Jump(exit); + } + Bind(¬StableJSArray); + { + DebugPrint(glue, {Int64(999)}); + DEFVARIABLE(j, VariableType::INT64(), Int64(0)); + Label loopHead(env); + Label loopEnd(env); + Label next(env); + Label loopExit(env); + Jump(&loopHead); + LoopBegin(&loopHead); + { + Branch(Int64LessThan(*j, thisArrLen), &next, &loopExit); + Bind(&next); + + GateRef thisEles = GetElementsArray(thisValue); + GateRef kValue = GetValueFromTaggedArray(thisEles, *j); + GateRef key = IntToTaggedPtr(*j); + Label hasException(env); + Label notHasException(env); + GateRef retValue = JSCallDispatch(glue, callbackFnHandle, Int32(NUM_MANDATORY_JSFUNC_ARGS), 0, + Circuit::NullGate(), JSCallMode::CALL_THIS_ARG3_WITH_RETURN, { argHandle, kValue, key, thisValue }); + Branch(TaggedIsException(retValue), &hasException, ¬HasException); + Bind(&hasException); + { + result->WriteVariable(retValue); + Jump(exit); + } + Bind(¬HasException); + { + Label find(env); + Branch(TaggedIsTrue(FastToBoolean(retValue)), &find, &loopEnd); + Bind(&find); + { + result->WriteVariable(key); + Jump(exit); + } + } + } + Bind(&loopEnd); + j = Int64Add(*j, Int64(1)); + LoopEnd(&loopHead); + Bind(&loopExit); + Jump(exit); + } +} + void BuiltinsArrayStubBuilder::Push(GateRef glue, GateRef thisValue, GateRef numArgs, Variable *result, Label *exit, Label *slowPath) { @@ -293,6 +834,78 @@ void BuiltinsArrayStubBuilder::Push(GateRef glue, GateRef thisValue, Jump(exit); } +GateRef BuiltinsArrayStubBuilder::IsConcatSpreadable(GateRef glue, GateRef obj) +{ + auto env = GetEnvironment(); + Label entry(env); + env->SubCfgEntry(&entry); + DEFVARIABLE(result, VariableType::BOOL(), False()); + Label exit(env); + Label isEcmaObj(env); + Branch(IsEcmaObject(obj), &isEcmaObj, &exit); + Bind(&isEcmaObj); + { + GateRef glueGlobalEnvOffset = IntPtr(JSThread::GlueData::GetGlueGlobalEnvOffset(env->Is32Bit())); + GateRef glueGlobalEnv = Load(VariableType::NATIVE_POINTER(), glue, glueGlobalEnvOffset); + GateRef isConcatsprKey = + GetGlobalEnvValue(VariableType::JS_ANY(), glueGlobalEnv, GlobalEnv::ISCONCAT_SYMBOL_INDEX); + AccessObjectStubBuilder builder(this); + GateRef spreadable = + builder.LoadObjByValue(glue, obj, isConcatsprKey, Undefined(), Int32(0), ProfileOperation()); + Label isDefined(env); + Label isUnDefined(env); + Branch(TaggedIsUndefined(spreadable), &isUnDefined, &isDefined); + Bind(&isUnDefined); + { + Label IsArray(env); + Branch(IsJsArray(obj), &IsArray, &exit); + Bind(&IsArray); + result = True(); + Jump(&exit); + } + Bind(&isDefined); + { + result = TaggedIsTrue(spreadable); + Jump(&exit); + } + } + Bind(&exit); + auto res = *result; + env->SubCfgExit(); + return res; +} + +GateRef BuiltinsArrayStubBuilder::NewArray(GateRef glue, GateRef count) +{ + auto env = GetEnvironment(); + Label entry(env); + env->SubCfgEntry(&entry); + DEFVARIABLE(result, VariableType::JS_POINTER(), Undefined()); + Label exit(env); + Label setProperties(env); + GateRef glueGlobalEnvOffset = IntPtr(JSThread::GlueData::GetGlueGlobalEnvOffset(env->Is32Bit())); + GateRef glueGlobalEnv = Load(VariableType::NATIVE_POINTER(), glue, glueGlobalEnvOffset); + auto arrayFunc = GetGlobalEnvValue(VariableType::JS_ANY(), glueGlobalEnv, GlobalEnv::ARRAY_FUNCTION_INDEX); + GateRef intialHClass = Load(VariableType::JS_ANY(), arrayFunc, IntPtr(JSFunction::PROTO_OR_DYNCLASS_OFFSET)); + NewObjectStubBuilder newBuilder(this); + newBuilder.SetParameters(glue, 0); + result = newBuilder.NewJSArrayWithSize(intialHClass, count); + Branch(TaggedIsException(*result), &exit, &setProperties); + Bind(&setProperties); + { + GateRef lengthOffset = IntPtr(JSArray::LENGTH_OFFSET); + Store(VariableType::INT32(), glue, *result, lengthOffset, TruncInt64ToInt32(count)); + GateRef accessor = GetGlobalConstantValue(VariableType::JS_ANY(), glue, ConstantIndex::ARRAY_LENGTH_ACCESSOR); + SetPropertyInlinedProps(glue, *result, intialHClass, accessor, Int32(JSArray::LENGTH_INLINE_PROPERTY_INDEX)); + SetExtensibleToBitfield(glue, *result, true); + Jump(&exit); + } + Bind(&exit); + auto res = *result; + env->SubCfgExit(); + return res; +} + void BuiltinsArrayStubBuilder::Includes(GateRef glue, GateRef thisValue, GateRef numArgs, Variable *result, Label *exit, Label *slowPath) { diff --git a/ecmascript/compiler/builtins/builtins_array_stub_builder.h b/ecmascript/compiler/builtins/builtins_array_stub_builder.h index c25fa4444a02ea831802ef748f003ea455063028..0587eb5075e73525331bede41e6638ea116e88c1 100644 --- a/ecmascript/compiler/builtins/builtins_array_stub_builder.h +++ b/ecmascript/compiler/builtins/builtins_array_stub_builder.h @@ -36,6 +36,12 @@ public: void Filter(GateRef glue, GateRef thisValue, GateRef numArgs, Variable *result, Label *exit, Label *slowPath); + void Find(GateRef glue, GateRef thisValue, GateRef numArgs, + Variable *result, Label *exit, Label *slowPath); + + void FindIndex(GateRef glue, GateRef thisValue, GateRef numArgs, + Variable *result, Label *exit, Label *slowPath); + void ForEach(GateRef glue, GateRef thisValue, GateRef numArgs, Variable *result, Label *exit, Label *slowPath); @@ -48,6 +54,9 @@ public: void Slice(GateRef glue, GateRef thisValue, GateRef numArgs, Variable *result, Label *exit, Label *slowPath); + void Values(GateRef glue, GateRef thisValue, GateRef numArgs, + Variable *result, Label *exit, Label *slowPath); + void Reverse(GateRef glue, GateRef thisValue, GateRef numArgs, Variable *result, Label *exit, Label *slowPath); @@ -55,6 +64,10 @@ public: Variable *result, Label *exit, Label *slowPath); void Includes(GateRef glue, GateRef thisValue, GateRef numArgs, Variable *result, Label *exit, Label *slowPath); + + GateRef IsConcatSpreadable(GateRef glue, GateRef obj); + + GateRef NewArray(GateRef glue, GateRef count); private: static constexpr uint32_t MAX_LENGTH_ZERO = 0; static constexpr uint32_t MAX_LENGTH_ONE = 1; diff --git a/ecmascript/compiler/builtins/builtins_call_signature.h b/ecmascript/compiler/builtins/builtins_call_signature.h index f3ea44e2170d1af982ddabcfee5a0cb2f6761654..b4b93746da9234c07951bcb6e41e6028840f4321 100644 --- a/ecmascript/compiler/builtins/builtins_call_signature.h +++ b/ecmascript/compiler/builtins/builtins_call_signature.h @@ -62,10 +62,13 @@ namespace panda::ecmascript::kungfu { V(FunctionPrototypeApply) \ V(ArrayConcat) \ V(ArrayFilter) \ + V(ArrayFind) \ + V(ArrayFindIndex) \ V(ArrayForEach) \ V(ArrayIndexOf) \ V(ArrayLastIndexOf) \ V(ArraySlice) \ + V(ArrayValues) \ V(ArrayReverse) \ V(ArrayPush) \ V(ArrayIncludes) \ diff --git a/ecmascript/compiler/builtins/builtins_string_stub_builder.cpp b/ecmascript/compiler/builtins/builtins_string_stub_builder.cpp index 46585a19d96cacc8ac89da3bfcbf006865bc9bb8..92760a981c1602844d0a699012467da0340f3f1b 100644 --- a/ecmascript/compiler/builtins/builtins_string_stub_builder.cpp +++ b/ecmascript/compiler/builtins/builtins_string_stub_builder.cpp @@ -1876,7 +1876,7 @@ void BuiltinsStringStubBuilder::LocaleCompare([[maybe_unused]] GateRef glue, Gat GateRef options = GetCallArg2(numArgs); GateRef localesIsUndef = TaggedIsUndefined(locales); GateRef optionsIsUndef = TaggedIsUndefined(options); - GateRef cacheable = BoolAnd(BoolOr(localesIsUndef, TaggedObjectIsString(locales)), optionsIsUndef); + GateRef cacheable = BoolAnd(BoolOr(localesIsUndef, TaggedIsString(locales)), optionsIsUndef); Label optionsIsString(env); Label cacheAble(env); Label uncacheable(env); diff --git a/ecmascript/compiler/builtins/builtins_stubs.cpp b/ecmascript/compiler/builtins/builtins_stubs.cpp index 5d1beb8981dea000fce3c4a65c01b777eed1c3eb..dc9df9fcde14a70a9bf2d6602c4e6ad506785767 100644 --- a/ecmascript/compiler/builtins/builtins_stubs.cpp +++ b/ecmascript/compiler/builtins/builtins_stubs.cpp @@ -278,14 +278,17 @@ DECLARE_BUILTINS(Array##Method) } #define BUILTINS_WITH_ARRAY_STUB_BUILDER(V) \ - V(Concat, JS_POINTER) \ + V(Concat, JS_ANY) \ V(Filter, JS_POINTER) \ + V(Find, JS_ANY) \ + V(FindIndex, JS_ANY) \ V(ForEach, JS_ANY) \ V(IndexOf, JS_ANY) \ V(LastIndexOf, JS_ANY) \ V(Slice, JS_POINTER) \ V(Reverse, JS_POINTER) \ V(Push, JS_ANY) \ + V(Values, JS_POINTER) \ V(Includes, JS_ANY) BUILTINS_WITH_ARRAY_STUB_BUILDER(DECLARE_BUILTINS_WITH_ARRAY_STUB_BUILDER) diff --git a/ecmascript/compiler/bytecodes.cpp b/ecmascript/compiler/bytecodes.cpp index b4bdc7359696a1b52114c61eb764206c17a1d594..7b0a094e33088e288f92ee4fac51a777f3490a3e 100644 --- a/ecmascript/compiler/bytecodes.cpp +++ b/ecmascript/compiler/bytecodes.cpp @@ -210,6 +210,7 @@ BytecodeMetaData BytecodeMetaData::InitBytecodeMetaData(const uint8_t *pc) case EcmaOpcode::GREATER_IMM8_V8: case EcmaOpcode::GREATEREQ_IMM8_V8: case EcmaOpcode::STRICTEQ_IMM8_V8: + case EcmaOpcode::STRICTNOTEQ_IMM8_V8: case EcmaOpcode::TONUMERIC_IMM8: case EcmaOpcode::ISTRUE: case EcmaOpcode::ISFALSE: diff --git a/ecmascript/compiler/circuit_builder.cpp b/ecmascript/compiler/circuit_builder.cpp index 253d4141118755dd8544b3f93be66c15311c7d0c..f34e573fad813925fc07340a344619aeeac915de 100644 --- a/ecmascript/compiler/circuit_builder.cpp +++ b/ecmascript/compiler/circuit_builder.cpp @@ -31,6 +31,7 @@ #include "ecmascript/jspandafile/program_object.h" #include "ecmascript/mem/region.h" #include "ecmascript/method.h" +#include "ecmascript/js_array_iterator.h" namespace panda::ecmascript::kungfu { @@ -613,6 +614,24 @@ void CircuitBuilder::SetCachedHclassOfForInIterator(GateRef glue, GateRef iter, Store(VariableType::JS_ANY(), glue, iter, offset, hclass); } +void CircuitBuilder::SetNextIndexOfArrayIterator(GateRef glue, GateRef iter, GateRef nextIndex) +{ + GateRef offset = IntPtr(JSArrayIterator::NEXT_INDEX_OFFSET); + Store(VariableType::INT32(), glue, iter, offset, nextIndex); +} + +void CircuitBuilder::SetIteratedArrayOfArrayIterator(GateRef glue, GateRef iter, GateRef iteratedArray) +{ + GateRef offset = IntPtr(JSArrayIterator::ITERATED_ARRAY_OFFSET); + Store(VariableType::JS_ANY(), glue, iter, offset, iteratedArray); +} + +void CircuitBuilder::SetBitFieldOfArrayIterator(GateRef glue, GateRef iter, GateRef kind) +{ + GateRef offset = IntPtr(JSArrayIterator::BIT_FIELD_OFFSET); + Store(VariableType::INT32(), glue, iter, offset, kind); +} + void CircuitBuilder::IncreaseInteratorIndex(GateRef glue, GateRef iter, GateRef index) { GateRef newIndex = Int32Add(index, Int32(1)); diff --git a/ecmascript/compiler/circuit_builder.h b/ecmascript/compiler/circuit_builder.h index 2c0c143695ee800661337688f3a3d2d3b1dabe75..7b91b86ef663879872c8a501991414fc84f2abc1 100644 --- a/ecmascript/compiler/circuit_builder.h +++ b/ecmascript/compiler/circuit_builder.h @@ -258,6 +258,9 @@ public: void SetObjectOfForInIterator(GateRef glue, GateRef iter, GateRef object); void SetCachedHclassOfForInIterator(GateRef glue, GateRef iter, GateRef hclass); void IncreaseInteratorIndex(GateRef glue, GateRef iter, GateRef index); + void SetNextIndexOfArrayIterator(GateRef glue, GateRef iter, GateRef nextIndex); + void SetIteratedArrayOfArrayIterator(GateRef glue, GateRef iter, GateRef iteratedArray); + void SetBitFieldOfArrayIterator(GateRef glue, GateRef iter, GateRef kind); GateRef GetHasChanged(GateRef object); GateRef GetAccessorHasChanged(GateRef object); GateRef HasDeleteProperty(GateRef hClass); @@ -580,6 +583,7 @@ public: inline GateRef TaggedIsStoreTSHandler(GateRef x); inline GateRef TaggedIsTransWithProtoHandler(GateRef x); inline GateRef TaggedIsUndefinedOrNull(GateRef x); + inline GateRef TaggedIsNotUndefinedAndNull(GateRef x); inline GateRef TaggedIsTrue(GateRef x); inline GateRef TaggedIsFalse(GateRef x); inline GateRef TaggedIsNull(GateRef x); diff --git a/ecmascript/compiler/file_generators.cpp b/ecmascript/compiler/file_generators.cpp index e7dd1898d32a28b5a4a809d365322180c2a2076c..6ebe21474cda95c1af776310a406d939a76f69fd 100644 --- a/ecmascript/compiler/file_generators.cpp +++ b/ecmascript/compiler/file_generators.cpp @@ -417,7 +417,7 @@ bool AOTFileGenerator::CreateDirIfNotExist(const std::string &filename) return panda::ecmascript::SetDirModeAsDefault(path); } -void AOTFileGenerator::SaveAOTFile(const std::string &filename) +void AOTFileGenerator::SaveAOTFile(const std::string &filename, const std::string &appSignature) { if (aotInfo_.GetTotalCodeSize() == 0) { LOG_COMPILER(WARN) << "error: code size of generated an file is empty!"; @@ -434,7 +434,7 @@ void AOTFileGenerator::SaveAOTFile(const std::string &filename) if (!panda::ecmascript::SetFileModeAsDefault(filename)) { LOG_COMPILER(ERROR) << "Fail to set an file mode:" << filename; } - panda::ecmascript::CodeSignForAOTFile(filename); + panda::ecmascript::CodeSignatureForAOTFile(filename, appSignature); } void AOTFileGenerator::SaveSnapshotFile() diff --git a/ecmascript/compiler/file_generators.h b/ecmascript/compiler/file_generators.h index 9f01046d061094f9e3e97b43a2061c7777cbc57c..4789f606c28fcde650c905d3178756c3aed27bf0 100644 --- a/ecmascript/compiler/file_generators.h +++ b/ecmascript/compiler/file_generators.h @@ -187,7 +187,7 @@ public: bool SetFileModeAsDefault(const std::string &filename); // save function for aot files containing normal func translated from JS/TS - void SaveAOTFile(const std::string &filename); + void SaveAOTFile(const std::string &filename, const std::string &appSignature); void SaveSnapshotFile(); diff --git a/ecmascript/compiler/gate_accessor.cpp b/ecmascript/compiler/gate_accessor.cpp index d96a8f6ce111c5bce9faba04a97660dc9b901d51..81c820cff45910a5eb437758b0eaf37c9e3042be 100644 --- a/ecmascript/compiler/gate_accessor.cpp +++ b/ecmascript/compiler/gate_accessor.cpp @@ -607,7 +607,7 @@ void GateAccessor::TrySetArrayElementsLength(GateRef gate, uint32_t length) Gate *gatePtr = circuit_->LoadGatePtr(gate); OpCode op = GetOpCode(gate); if (op == OpCode::JS_BYTECODE) { - const_cast(gatePtr->GetJSBytecodeMetaData())->SetElementsLength(length); + const_cast(gatePtr->GetJSBytecodeMetaData())->SetElementsLength(length); } } diff --git a/ecmascript/compiler/mcr_circuit_builder.h b/ecmascript/compiler/mcr_circuit_builder.h index 3b7ded3eccc1bca938694589f7fd0507eebd1bda..e0077bba389e79a63034e11cd8156bffe1601d7a 100644 --- a/ecmascript/compiler/mcr_circuit_builder.h +++ b/ecmascript/compiler/mcr_circuit_builder.h @@ -389,6 +389,16 @@ GateRef CircuitBuilder::TaggedIsUndefinedOrNull(GateRef x) return result; } +GateRef CircuitBuilder::TaggedIsNotUndefinedAndNull(GateRef x) +{ + x = ChangeTaggedPointerToInt64(x); + GateRef heapObjMask = Int64(JSTaggedValue::TAG_HEAPOBJECT_MASK); + GateRef tagSpecial = Int64(JSTaggedValue::TAG_SPECIAL); + GateRef andGate = Int64And(x, heapObjMask); + GateRef result = NotEqual(andGate, tagSpecial); + return result; +} + GateRef CircuitBuilder::TaggedTrue() { return GetCircuit()->GetConstantGate(MachineType::I64, JSTaggedValue::VALUE_TRUE, GateType::TaggedValue()); diff --git a/ecmascript/compiler/mcr_gate_meta_data.h b/ecmascript/compiler/mcr_gate_meta_data.h index 2b00af0466feea810a76efe1b9bb241a6edf3966..0d4621fdfd3ff5ad1b333c4e157c470e6a9cf4e7 100644 --- a/ecmascript/compiler/mcr_gate_meta_data.h +++ b/ecmascript/compiler/mcr_gate_meta_data.h @@ -44,6 +44,7 @@ namespace panda::ecmascript::kungfu { V(TYPED_EQ) \ V(TYPED_NOTEQ) \ V(TYPED_STRICTEQ) \ + V(TYPED_STRICTNOTEQ) \ V(TYPED_SHL) \ V(TYPED_SHR) \ V(TYPED_ASHR) \ diff --git a/ecmascript/compiler/ntype_hcr_lowering.cpp b/ecmascript/compiler/ntype_hcr_lowering.cpp index 0ad57b78af9d97a27c39d0c60fa338d65797ae78..9cc2d73de35fe155dbcff73de159ecfd7c35d35c 100644 --- a/ecmascript/compiler/ntype_hcr_lowering.cpp +++ b/ecmascript/compiler/ntype_hcr_lowering.cpp @@ -46,24 +46,20 @@ void NTypeHCRLowering::LowerCreateArray(GateRef gate, GateRef glue) { Environment env(gate, circuit_, &builder_); if (acc_.GetArraySize(gate) == 0) { - LowerCreateEmptyArray(gate, glue); + LowerCreateEmptyArray(gate); } else { LowerCreateArrayWithOwn(gate, glue); } } -void NTypeHCRLowering::LowerCreateEmptyArray(GateRef gate, GateRef glue) +void NTypeHCRLowering::LowerCreateEmptyArray(GateRef gate) { GateRef length = builder_.Int32(0); GateRef elements = Circuit::NullGate(); GateRef value = acc_.GetValueIn(gate, 0); - auto elementsLength = static_cast(acc_.GetConstantValue(value)); - if (elementsLength > 0) { - elements = CreateElementsWithLength(gate, glue, elementsLength); - } else { - elements = builder_.GetGlobalConstantValue(ConstantIndex::EMPTY_ARRAY_OBJECT_INDEX); - } - auto array = NewJSArrayLiteral(gate, elements, length); + auto hintLength = static_cast(acc_.GetConstantValue(value)); + elements = builder_.GetGlobalConstantValue(ConstantIndex::EMPTY_ARRAY_OBJECT_INDEX); + auto array = NewJSArrayLiteral(gate, elements, length, hintLength); acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), array); } @@ -137,7 +133,7 @@ GateRef NTypeHCRLowering::CreateElementsWithLength(GateRef gate, GateRef glue, s return elements; } -GateRef NTypeHCRLowering::NewJSArrayLiteral(GateRef gate, GateRef elements, GateRef length) +GateRef NTypeHCRLowering::NewJSArrayLiteral(GateRef gate, GateRef elements, GateRef length, uint32_t hintLength) { ElementsKind kind = acc_.GetArrayMetaDataAccessor(gate).GetElementsKind(); GateRef hclass = Circuit::NullGate(); @@ -172,7 +168,12 @@ GateRef NTypeHCRLowering::NewJSArrayLiteral(GateRef gate, GateRef elements, Gate builder_.StoreConstOffset(VariableType::JS_POINTER(), array, JSObject::PROPERTIES_OFFSET, emptyArray); builder_.StoreConstOffset(VariableType::JS_POINTER(), array, JSObject::ELEMENTS_OFFSET, elements); builder_.StoreConstOffset(VariableType::INT32(), array, JSArray::LENGTH_OFFSET, length); - builder_.StoreConstOffset(VariableType::INT64(), array, JSArray::TRACK_INFO_OFFSET, builder_.Undefined()); + if (hintLength > 0) { + builder_.StoreConstOffset(VariableType::INT64(), array, JSArray::TRACK_INFO_OFFSET, + builder_.Int64(JSTaggedValue(hintLength).GetRawData())); + } else { + builder_.StoreConstOffset(VariableType::INT64(), array, JSArray::TRACK_INFO_OFFSET, builder_.Undefined()); + } builder_.StoreConstOffset(VariableType::JS_POINTER(), array, lengthAccessorOffset, accessor); builder_.FinishAllocate(); return array; diff --git a/ecmascript/compiler/ntype_hcr_lowering.h b/ecmascript/compiler/ntype_hcr_lowering.h index 04af7abc73c29f0fe3a508110f97392d038672b7..0fc6d19c8e5c801e78e4f9063dfa7fcc5001555a 100644 --- a/ecmascript/compiler/ntype_hcr_lowering.h +++ b/ecmascript/compiler/ntype_hcr_lowering.h @@ -44,13 +44,13 @@ private: void Lower(GateRef gate); void LowerCreateArray(GateRef gate, GateRef glue); void LowerCreateArrayWithBuffer(GateRef gate, GateRef glue); - void LowerCreateEmptyArray(GateRef gate, GateRef glue); + void LowerCreateEmptyArray(GateRef gate); void LowerCreateArrayWithOwn(GateRef gate, GateRef glue); void LowerStoreModuleVar(GateRef gate, GateRef glue); void LowerLdLocalModuleVar(GateRef gate); GateRef LoadFromConstPool(GateRef jsFunc, size_t index, size_t valVecType); - GateRef NewJSArrayLiteral(GateRef gate, GateRef elements, GateRef length); + GateRef NewJSArrayLiteral(GateRef gate, GateRef elements, GateRef length, uint32_t hintLength = 0); GateRef NewTaggedArray(size_t length); GateRef NewTaggedArray(GateRef length); GateRef CreateElementsWithLength(GateRef gate, GateRef glue, size_t arrayLength); diff --git a/ecmascript/compiler/number_speculative_lowering.cpp b/ecmascript/compiler/number_speculative_lowering.cpp index 74434b50d08c631711d96ea3f7be81a225bc3314..d88ca31d17801494732477d7494e979b17c19660 100644 --- a/ecmascript/compiler/number_speculative_lowering.cpp +++ b/ecmascript/compiler/number_speculative_lowering.cpp @@ -116,12 +116,14 @@ void NumberSpeculativeLowering::VisitTypedBinaryOp(GateRef gate) } auto op = acc_.GetTypedBinaryOp(gate); switch (op) { - case TypedBinOp::TYPED_STRICTEQ: { - VisitStrictEqual(gate); + case TypedBinOp::TYPED_STRICTEQ: + case TypedBinOp::TYPED_STRICTNOTEQ: { + VisitStrictEqualOrStrictNotEqual(gate); break; } - case TypedBinOp::TYPED_EQ: { - VisitEqual(gate); + case TypedBinOp::TYPED_EQ: + case TypedBinOp::TYPED_NOTEQ: { + VisitEqualOrNotEqual(gate); break; } default: { @@ -133,21 +135,21 @@ void NumberSpeculativeLowering::VisitTypedBinaryOp(GateRef gate) } } -void NumberSpeculativeLowering::VisitEqual(GateRef gate) +void NumberSpeculativeLowering::VisitEqualOrNotEqual(GateRef gate) { if (acc_.HasNumberType(gate)) { VisitNumberBinaryOp(gate); } else { - VisitUndefinedEq(gate); + VisitUndefinedEqOrUndefinedNotEq(gate); } } -void NumberSpeculativeLowering::VisitStrictEqual(GateRef gate) +void NumberSpeculativeLowering::VisitStrictEqualOrStrictNotEqual(GateRef gate) { if (acc_.HasNumberType(gate)) { VisitNumberBinaryOp(gate); } else { - VisitUndefinedStrictEq(gate); + VisitUndefinedStrictEqOrUndefinedStrictNotEq(gate); } } @@ -195,6 +197,10 @@ void NumberSpeculativeLowering::VisitNumberBinaryOp(GateRef gate) VisitNumberCompare(gate); break; } + case TypedBinOp::TYPED_STRICTNOTEQ: { + VisitNumberCompare(gate); + break; + } case TypedBinOp::TYPED_SHL: { VisitNumberShift(gate); break; @@ -476,26 +482,39 @@ void NumberSpeculativeLowering::VisitBooleanJump(GateRef gate) acc_.ReplaceGate(gate, ifBranch, acc_.GetDep(gate), Circuit::NullGate()); } -void NumberSpeculativeLowering::VisitUndefinedStrictEq(GateRef gate) +void NumberSpeculativeLowering::VisitUndefinedStrictEqOrUndefinedStrictNotEq(GateRef gate) { - ASSERT(acc_.GetTypedBinaryOp(gate) == TypedBinOp::TYPED_STRICTEQ); + ASSERT(acc_.GetTypedBinaryOp(gate) == TypedBinOp::TYPED_STRICTEQ || + acc_.GetTypedBinaryOp(gate) == TypedBinOp::TYPED_STRICTNOTEQ); GateRef left = acc_.GetValueIn(gate, 0); GateRef right = acc_.GetValueIn(gate, 1); ASSERT(acc_.IsUndefinedOrNull(left) || acc_.IsUndefinedOrNull(right)); - GateRef result = builder_.Equal(left, right); + GateRef result = Circuit::NullGate(); + if (acc_.GetTypedBinaryOp(gate) == TypedBinOp::TYPED_STRICTEQ) { + result = builder_.Equal(left, right); + } else { + result = builder_.NotEqual(left, right); + } + ASSERT(result != Circuit::NullGate()); acc_.SetMachineType(gate, MachineType::I1); acc_.SetGateType(gate, GateType::NJSValue()); acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), result); } -void NumberSpeculativeLowering::VisitUndefinedEq(GateRef gate) +void NumberSpeculativeLowering::VisitUndefinedEqOrUndefinedNotEq(GateRef gate) { - ASSERT(acc_.GetTypedBinaryOp(gate) == TypedBinOp::TYPED_EQ); + ASSERT(acc_.GetTypedBinaryOp(gate) == TypedBinOp::TYPED_EQ || + acc_.GetTypedBinaryOp(gate) == TypedBinOp::TYPED_NOTEQ); GateRef left = acc_.GetValueIn(gate, 0); GateRef right = acc_.GetValueIn(gate, 1); ASSERT(acc_.IsUndefinedOrNull(left) || acc_.IsUndefinedOrNull(right)); GateRef valueGate = acc_.IsUndefinedOrNull(left) ? right : left; - GateRef result = builder_.TaggedIsUndefinedOrNull(valueGate); + GateRef result = Circuit::NullGate(); + if (acc_.GetTypedBinaryOp(gate) == TypedBinOp::TYPED_EQ) { + result = builder_.TaggedIsUndefinedOrNull(valueGate); + } else { + result = builder_.TaggedIsNotUndefinedAndNull(valueGate); + } acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), result); } @@ -730,6 +749,7 @@ GateRef NumberSpeculativeLowering::CompareInts(GateRef left, GateRef right) condition = builder_.Int32Equal(left, right); break; case TypedBinOp::TYPED_NOTEQ: + case TypedBinOp::TYPED_STRICTNOTEQ: condition = builder_.Int32NotEqual(left, right); break; default: @@ -768,6 +788,13 @@ GateRef NumberSpeculativeLowering::CompareDoubles(GateRef left, GateRef right) condition = builder_.BoolAnd(builder_.BoolAnd(leftNotNan, rightNotNan), doubleEqual); break; } + case TypedBinOp::TYPED_STRICTNOTEQ: { + GateRef leftNotNan = builder_.DoubleIsNAN(left); + GateRef rightNotNan = builder_.DoubleIsNAN(right); + GateRef doubleNotEqual = builder_.DoubleNotEqual(left, right); + condition = builder_.BoolOr(builder_.BoolOr(leftNotNan, rightNotNan), doubleNotEqual); + break; + } default: break; } diff --git a/ecmascript/compiler/number_speculative_lowering.h b/ecmascript/compiler/number_speculative_lowering.h index fe5e1cebdf438e6789373c708794ed1c84fcdf18..28b3c3c55646bfbfb0b592afdfb7e533970de558 100644 --- a/ecmascript/compiler/number_speculative_lowering.h +++ b/ecmascript/compiler/number_speculative_lowering.h @@ -43,9 +43,9 @@ private: void VisitTypedConditionJump(GateRef gate); void VisitConstant(GateRef gate); void VisitPhi(GateRef gate); - void VisitUndefinedStrictEq(GateRef gate); - void VisitUndefinedEq(GateRef gate); - void VisitEqual(GateRef gate); + void VisitUndefinedStrictEqOrUndefinedStrictNotEq(GateRef gate); + void VisitUndefinedEqOrUndefinedNotEq(GateRef gate); + void VisitEqualOrNotEqual(GateRef gate); void VisitCallBuiltins(GateRef gate); void VisitRangeGuard(GateRef gate); void VisitRangeCheckPredicate(GateRef gate); @@ -70,7 +70,7 @@ private: void VisitNumberMod(GateRef gate); void VisitBooleanJump(GateRef gate); void VisitIsTrueOrFalse(GateRef gate, bool flag); - void VisitStrictEqual(GateRef gate); + void VisitStrictEqualOrStrictNotEqual(GateRef gate); template void VisitStringCompare(GateRef gate); diff --git a/ecmascript/compiler/number_speculative_retype.cpp b/ecmascript/compiler/number_speculative_retype.cpp index ad1e498b0f33dc5c427f1f8c6252b19357acd4c6..9473a0feedaecd0dafc82ca9b67c54bee34e3fd2 100644 --- a/ecmascript/compiler/number_speculative_retype.cpp +++ b/ecmascript/compiler/number_speculative_retype.cpp @@ -160,28 +160,32 @@ GateRef NumberSpeculativeRetype::VisitTypedBinaryOp(GateRef gate) } if (acc_.GetTypedBinaryOp(gate) != TypedBinOp::TYPED_STRICTEQ && - acc_.GetTypedBinaryOp(gate) != TypedBinOp::TYPED_EQ) { + acc_.GetTypedBinaryOp(gate) != TypedBinOp::TYPED_STRICTNOTEQ && + acc_.GetTypedBinaryOp(gate) != TypedBinOp::TYPED_EQ && + acc_.GetTypedBinaryOp(gate) != TypedBinOp::TYPED_NOTEQ) { if (acc_.HasPrimitiveNumberType(gate)) { return VisitNumberBinaryOp(gate); } } - return VisitEqualOrStrictEqual(gate); + return VisitEqualCompareOrNotEqualCompare(gate); } -GateRef NumberSpeculativeRetype::VisitEqualOrStrictEqual(GateRef gate) +GateRef NumberSpeculativeRetype::VisitEqualCompareOrNotEqualCompare(GateRef gate) { if (acc_.HasNumberType(gate)) { return VisitNumberBinaryOp(gate); } else { - return VisitUndefinedEqOrStrictEq(gate); + return VisitUndefinedEqualCompareOrUndefinedNotEqualCompare(gate); } } -GateRef NumberSpeculativeRetype::VisitUndefinedEqOrStrictEq(GateRef gate) +GateRef NumberSpeculativeRetype::VisitUndefinedEqualCompareOrUndefinedNotEqualCompare(GateRef gate) { ASSERT(acc_.GetTypedBinaryOp(gate) == TypedBinOp::TYPED_STRICTEQ || - acc_.GetTypedBinaryOp(gate) == TypedBinOp::TYPED_EQ); + acc_.GetTypedBinaryOp(gate) == TypedBinOp::TYPED_STRICTNOTEQ || + acc_.GetTypedBinaryOp(gate) == TypedBinOp::TYPED_EQ || + acc_.GetTypedBinaryOp(gate) == TypedBinOp::TYPED_NOTEQ); GateRef left = acc_.GetValueIn(gate, 0); GateRef right = acc_.GetValueIn(gate, 1); ASSERT((acc_.IsUndefinedOrNull(left)) || (acc_.IsUndefinedOrNull(right))); @@ -259,6 +263,19 @@ GateRef NumberSpeculativeRetype::VisitStringAdd(GateRef gate) if (IsRetype()) { return SetOutputType(gate, GateType::StringType()); } + if (IsConvert()) { + Environment env(gate, circuit_, &builder_); + size_t valueNum = acc_.GetNumValueIn(gate); + for (size_t i = 0; i < valueNum; ++i) { + GateRef input = acc_.GetValueIn(gate, i); + TypeInfo inputInfo = GetOutputTypeInfo(input); + if (inputInfo == TypeInfo::CHAR) { + GateRef glue = acc_.GetGlueFromArgList(); + input = builder_.CallStub(glue, gate, CommonStubCSigns::CreateStringBySingleCharCode, { glue, input }); + } + acc_.ReplaceValueIn(gate, input, i); + } + } return Circuit::NullGate(); } @@ -361,7 +378,8 @@ GateRef NumberSpeculativeRetype::VisitNumberBinaryOp(GateRef gate) case TypedBinOp::TYPED_GREATEREQ: case TypedBinOp::TYPED_EQ: case TypedBinOp::TYPED_NOTEQ: - case TypedBinOp::TYPED_STRICTEQ: { + case TypedBinOp::TYPED_STRICTEQ: + case TypedBinOp::TYPED_STRICTNOTEQ: { return VisitNumberCompare(gate); } case TypedBinOp::TYPED_SHL: diff --git a/ecmascript/compiler/number_speculative_retype.h b/ecmascript/compiler/number_speculative_retype.h index 6d83be0b491b1748edb0c3944b280485a4c909c2..b42a05ee8ab57284d4ab2b951557efea2edc738b 100644 --- a/ecmascript/compiler/number_speculative_retype.h +++ b/ecmascript/compiler/number_speculative_retype.h @@ -64,7 +64,7 @@ private: GateRef VisitTypedBinaryOp(GateRef gate); GateRef VisitNumberBinaryOp(GateRef gate); GateRef VisitStringBinaryOp(GateRef gate); - GateRef VisitUndefinedEqOrStrictEq(GateRef gate); + GateRef VisitUndefinedEqualCompareOrUndefinedNotEqualCompare(GateRef gate); GateRef VisitTypedUnaryOp(GateRef gate); GateRef VisitNumberMonocular(GateRef gate); @@ -95,7 +95,7 @@ private: GateRef VisitIsTrueOrFalse(GateRef gate); GateRef VisitWithConstantValue(GateRef gate, size_t ignoreIndex); GateRef VisitIntermediateValue(GateRef gate); - GateRef VisitEqualOrStrictEqual(GateRef gate); + GateRef VisitEqualCompareOrNotEqualCompare(GateRef gate); GateRef VisitStringCompare(GateRef gate); GateRef VisitStringAdd(GateRef gate); diff --git a/ecmascript/compiler/stub_builder-inl.h b/ecmascript/compiler/stub_builder-inl.h index 62c7623b410aec3b4ff3d9ab5b9a4b4ed993930a..69e7b58d879cde0ea35f7eb02fd1d9cfad1ecfcb 100644 --- a/ecmascript/compiler/stub_builder-inl.h +++ b/ecmascript/compiler/stub_builder-inl.h @@ -1163,25 +1163,6 @@ inline GateRef StubBuilder::IsString(GateRef obj) return res; } -inline GateRef StubBuilder::TaggedObjectIsString(GateRef obj) -{ - auto env = GetEnvironment(); - Label entryPass(env); - env->SubCfgEntry(&entryPass); - DEFVARIABLE(result, VariableType::BOOL(), False()); - Label heapObj(env); - Label exit(env); - GateRef isHeapObject = TaggedIsHeapObject(obj); - Branch(isHeapObject, &heapObj, &exit); - Bind(&heapObj); - result = env_->GetBuilder()->TaggedObjectIsString(obj); - Jump(&exit); - Bind(&exit); - auto ret = *result; - env->SubCfgExit(); - return ret; -} - inline GateRef StubBuilder::IsLineString(GateRef obj) { GateRef objectType = GetObjectType(LoadHClass(obj)); @@ -1550,6 +1531,21 @@ inline void StubBuilder::IncreaseInteratorIndex(GateRef glue, GateRef iter, Gate env_->GetBuilder()->IncreaseInteratorIndex(glue, iter, index); } +inline void StubBuilder::SetNextIndexOfArrayIterator(GateRef glue, GateRef iter, GateRef nextIndex) +{ + env_->GetBuilder()->SetNextIndexOfArrayIterator(glue, iter, nextIndex); +} + +inline void StubBuilder::SetIteratedArrayOfArrayIterator(GateRef glue, GateRef iter, GateRef iteratedArray) +{ + env_->GetBuilder()->SetIteratedArrayOfArrayIterator(glue, iter, iteratedArray); +} + +inline void StubBuilder::SetBitFieldOfArrayIterator(GateRef glue, GateRef iter, GateRef kind) +{ + env_->GetBuilder()->SetBitFieldOfArrayIterator(glue, iter, kind); +} + inline GateRef StubBuilder::IsField(GateRef attr) { return Int32Equal( diff --git a/ecmascript/compiler/stub_builder.cpp b/ecmascript/compiler/stub_builder.cpp index 89ab37f910d96ad4da4ae15d32799aaf42f57d63..5d303f2402f5b3c2d94c8a76b569ddee53acda9c 100644 --- a/ecmascript/compiler/stub_builder.cpp +++ b/ecmascript/compiler/stub_builder.cpp @@ -4353,6 +4353,35 @@ GateRef StubBuilder::FastGetPropertyByIndex(GateRef glue, GateRef obj, GateRef i return ret; } +GateRef StubBuilder::FastGetPropertyByValue(GateRef glue, GateRef obj, GateRef key, ProfileOperation callback) +{ + auto env = GetEnvironment(); + Label entry(env); + env->SubCfgEntry(&entry); + DEFVARIABLE(result, VariableType::JS_ANY(), Hole()); + Label exit(env); + Label fastPath(env); + Label slowPath(env); + + Branch(TaggedIsHeapObject(obj), &fastPath, &slowPath); + Bind(&fastPath); + { + result = GetPropertyByValue(glue, obj, key, callback); + Label notHole(env); + Branch(TaggedIsHole(*result), &slowPath, &exit); + } + Bind(&slowPath); + { + result = CallRuntime(glue, RTSTUB_ID(LoadICByValue), + { Undefined(), obj, key, IntToTaggedInt(Int32(0)) }); + Jump(&exit); + } + Bind(&exit); + auto ret = *result; + env->SubCfgExit(); + return ret; +} + void StubBuilder::FastSetPropertyByName(GateRef glue, GateRef obj, GateRef key, GateRef value, ProfileOperation callback) { @@ -4425,6 +4454,35 @@ void StubBuilder::FastSetPropertyByIndex(GateRef glue, GateRef obj, GateRef inde env->SubCfgExit(); } +GateRef StubBuilder::GetCtorPrototype(GateRef ctor) +{ + auto env = GetEnvironment(); + Label entry(env); + env->SubCfgEntry(&entry); + DEFVARIABLE(constructorPrototype, VariableType::JS_ANY(), Undefined()); + Label exit(env); + Label isHClass(env); + Label isPrototype(env); + + GateRef ctorProtoOrHC = Load(VariableType::JS_POINTER(), ctor, IntPtr(JSFunction::PROTO_OR_DYNCLASS_OFFSET)); + Branch(IsJSHClass(ctorProtoOrHC), &isHClass, &isPrototype); + Bind(&isHClass); + { + constructorPrototype = Load(VariableType::JS_POINTER(), ctorProtoOrHC, IntPtr(JSHClass::PROTOTYPE_OFFSET)); + Jump(&exit); + } + Bind(&isPrototype); + { + constructorPrototype = ctorProtoOrHC; + Jump(&exit); + } + + Bind(&exit); + auto ret = *constructorPrototype; + env->SubCfgExit(); + return ret; +} + GateRef StubBuilder::OrdinaryHasInstance(GateRef glue, GateRef target, GateRef obj) { auto env = GetEnvironment(); @@ -4474,10 +4532,32 @@ GateRef StubBuilder::OrdinaryHasInstance(GateRef glue, GateRef target, GateRef o Bind(&objIsEcmaObject); { // 4. Let P be Get(C, "prototype"). - auto prototypeString = GetGlobalConstantValue( - VariableType::JS_POINTER(), glue, ConstantIndex::PROTOTYPE_STRING_INDEX); + Label getCtorProtoSlowPath(env); + Label ctorIsJSFunction(env); + Label gotCtorPrototype(env); + DEFVARIABLE(constructorPrototype, VariableType::JS_ANY(), Undefined()); + Branch(IsJSFunction(target), &ctorIsJSFunction, &getCtorProtoSlowPath); + Bind(&ctorIsJSFunction); + { + Label getCtorProtoFastPath(env); + GateRef ctorProtoOrHC = Load(VariableType::JS_POINTER(), target, + IntPtr(JSFunction::PROTO_OR_DYNCLASS_OFFSET)); - GateRef constructorPrototype = FastGetPropertyByName(glue, target, prototypeString, ProfileOperation()); + Branch(TaggedIsHole(ctorProtoOrHC), &getCtorProtoSlowPath, &getCtorProtoFastPath); + Bind(&getCtorProtoFastPath); + { + constructorPrototype = GetCtorPrototype(target); + Jump(&gotCtorPrototype); + } + } + Bind(&getCtorProtoSlowPath); + { + auto prototypeString = GetGlobalConstantValue(VariableType::JS_POINTER(), glue, + ConstantIndex::PROTOTYPE_STRING_INDEX); + constructorPrototype = FastGetPropertyByName(glue, target, prototypeString, ProfileOperation()); + Jump(&gotCtorPrototype); + } + Bind(&gotCtorPrototype); // 5. ReturnIfAbrupt(P). // no throw exception, so needn't return @@ -4495,10 +4575,10 @@ GateRef StubBuilder::OrdinaryHasInstance(GateRef glue, GateRef target, GateRef o Label constructorPrototypeIsHeapObject(env); Label constructorPrototypeIsEcmaObject(env); Label constructorPrototypeNotEcmaObject(env); - Branch(TaggedIsHeapObject(constructorPrototype), &constructorPrototypeIsHeapObject, + Branch(TaggedIsHeapObject(*constructorPrototype), &constructorPrototypeIsHeapObject, &constructorPrototypeNotEcmaObject); Bind(&constructorPrototypeIsHeapObject); - Branch(TaggedObjectIsEcmaObject(constructorPrototype), &constructorPrototypeIsEcmaObject, + Branch(TaggedObjectIsEcmaObject(*constructorPrototype), &constructorPrototypeIsEcmaObject, &constructorPrototypeNotEcmaObject); Bind(&constructorPrototypeNotEcmaObject); { @@ -4525,7 +4605,7 @@ GateRef StubBuilder::OrdinaryHasInstance(GateRef glue, GateRef target, GateRef o Branch(TaggedIsNull(*object), &afterLoop, &loopHead); LoopBegin(&loopHead); { - GateRef isEqual = SameValue(glue, *object, constructorPrototype); + GateRef isEqual = SameValue(glue, *object, *constructorPrototype); Branch(isEqual, &strictEqual1, ¬StrictEqual1); Bind(&strictEqual1); @@ -5497,6 +5577,7 @@ GateRef StubBuilder::FastBinaryOp(GateRef glue, GateRef left, GateRef right, Branch(bothString, &stringAdd, &exit); Bind(&stringAdd); { + callback.ProfileOpType(Int32(PGOSampleType::StringType())); BuiltinsStringStubBuilder builtinsStringStubBuilder(this); result = builtinsStringStubBuilder.StringConcat(glue, left, right); Branch(HasPendingException(glue), &hasPendingException, &exit); @@ -6105,7 +6186,8 @@ GateRef StubBuilder::DeletePropertyOrThrow(GateRef glue, GateRef obj, GateRef va Branch(TaggedObjectIsEcmaObject(object), &objectIsEcmaObject, &isException); Bind(&objectIsEcmaObject); { - result = DeleteProperty(glue, obj, value); + GateRef keyValue = CallRuntime(glue, RTSTUB_ID(ToPropertyKey), {value}); + result = DeleteProperty(glue, obj, keyValue); Jump(&exit); } Bind(&isException); diff --git a/ecmascript/compiler/stub_builder.h b/ecmascript/compiler/stub_builder.h index 9fd812e3b222c280627cb2292c52472e7e8b854e..4d7bcd5dbc49c0c5dc72a70f5c4f134390c8e340 100644 --- a/ecmascript/compiler/stub_builder.h +++ b/ecmascript/compiler/stub_builder.h @@ -314,7 +314,6 @@ public: GateRef IsEcmaObject(GateRef obj); GateRef IsSymbol(GateRef obj); GateRef IsString(GateRef obj); - GateRef TaggedObjectIsString(GateRef obj); GateRef IsLineString(GateRef obj); GateRef IsSlicedString(GateRef obj); GateRef IsConstantString(GateRef obj); @@ -389,6 +388,7 @@ public: GateRef HclassIsTransitionHandler(GateRef hClass); GateRef HclassIsPropertyBox(GateRef hClass); GateRef PropAttrGetOffset(GateRef attr); + GateRef GetCtorPrototype(GateRef ctor); GateRef InstanceOf(GateRef glue, GateRef object, GateRef target, GateRef profileTypeInfo, GateRef slotId, ProfileOperation callback); GateRef OrdinaryHasInstance(GateRef glue, GateRef target, GateRef obj); @@ -588,6 +588,7 @@ public: GateRef GetPropertyByName(GateRef glue, GateRef receiver, GateRef key, ProfileOperation callback); GateRef FastGetPropertyByName(GateRef glue, GateRef obj, GateRef key, ProfileOperation callback); GateRef FastGetPropertyByIndex(GateRef glue, GateRef obj, GateRef index, ProfileOperation callback); + GateRef FastGetPropertyByValue(GateRef glue, GateRef obj, GateRef key, ProfileOperation callback); GateRef GetPropertyByValue(GateRef glue, GateRef receiver, GateRef keyValue, ProfileOperation callback); void FastSetPropertyByName(GateRef glue, GateRef obj, GateRef key, GateRef value, ProfileOperation callback = ProfileOperation()); @@ -664,6 +665,9 @@ public: void SetObjectOfForInIterator(GateRef glue, GateRef iter, GateRef object); void SetCachedHclassOfForInIterator(GateRef glue, GateRef iter, GateRef hclass); void IncreaseInteratorIndex(GateRef glue, GateRef iter, GateRef index); + void SetNextIndexOfArrayIterator(GateRef glue, GateRef iter, GateRef nextIndex); + void SetIteratedArrayOfArrayIterator(GateRef glue, GateRef iter, GateRef iteratedArray); + void SetBitFieldOfArrayIterator(GateRef glue, GateRef iter, GateRef kind); GateRef GetEnumCacheKind(GateRef glue, GateRef enumCache); GateRef GetEmptyArray(GateRef glue); GateRef IsEnumCacheValid(GateRef receiver, GateRef cachedHclass, GateRef kind); diff --git a/ecmascript/compiler/type_bytecode_lowering.cpp b/ecmascript/compiler/type_bytecode_lowering.cpp index e041ec31d7f86644207f199e051ad974bac19c70..c3da84ce4e073ba4e8fe850b93e6a2bcaae62e93 100644 --- a/ecmascript/compiler/type_bytecode_lowering.cpp +++ b/ecmascript/compiler/type_bytecode_lowering.cpp @@ -185,13 +185,16 @@ void TypeBytecodeLowering::Lower(GateRef gate) LowerTypedBinOp(gate); break; case EcmaOpcode::EQ_IMM8_V8: - LowerTypedEqOrStrictEq(gate); + LowerTypedEqOrNotEq(gate); break; case EcmaOpcode::STRICTEQ_IMM8_V8: - LowerTypedEqOrStrictEq(gate); + LowerTypedEqOrNotEq(gate); break; case EcmaOpcode::NOTEQ_IMM8_V8: - LowerTypedBinOp(gate, false); + LowerTypedEqOrNotEq(gate); + break; + case EcmaOpcode::STRICTNOTEQ_IMM8_V8: + LowerTypedEqOrNotEq(gate); break; case EcmaOpcode::SHL2_IMM8_V8: LowerTypedBinOp(gate); @@ -371,7 +374,7 @@ void TypeBytecodeLowering::LowerTypedUnOp(GateRef gate) } template -void TypeBytecodeLowering::LowerTypedEqOrStrictEq(GateRef gate) +void TypeBytecodeLowering::LowerTypedEqOrNotEq(GateRef gate) { GateRef left = acc_.GetValueIn(gate, 0); GateRef right = acc_.GetValueIn(gate, 1); @@ -1602,7 +1605,7 @@ void TypeBytecodeLowering::LowerTypedSuperCall(GateRef gate) } void TypeBytecodeLowering::SpeculateCallBuiltin(GateRef gate, GateRef func, const std::vector &args, - BuiltinsStubCSigns::ID id, bool isThrow) + BuiltinsStubCSigns::ID id, bool isThrow) { if (!Uncheck()) { builder_.CallTargetCheck(gate, func, builder_.IntPtr(static_cast(id)), args[0]); @@ -1830,7 +1833,7 @@ bool TypeBytecodeLowering::CanOptimizeAsFastCall(GateRef func) } void TypeBytecodeLowering::CheckFastCallThisCallTarget(GateRef gate, GateRef func, GlobalTSTypeRef funcGt, - GateType funcType, bool isNoGC) + GateType funcType, bool isNoGC) { if (noCheck_) { return; diff --git a/ecmascript/compiler/type_bytecode_lowering.h b/ecmascript/compiler/type_bytecode_lowering.h index 1f1b509edd6dd2a789d7f46356bf48bfd5524807..ab78538887f1f9c50173437161b2c0c6061a0622 100644 --- a/ecmascript/compiler/type_bytecode_lowering.h +++ b/ecmascript/compiler/type_bytecode_lowering.h @@ -101,7 +101,7 @@ private: template void LowerTypedUnOp(GateRef gate); template - void LowerTypedEqOrStrictEq(GateRef gate); + void LowerTypedEqOrNotEq(GateRef gate); void LowerTypeToNumeric(GateRef gate); void LowerPrimitiveTypeToNumber(GateRef gate); void LowerConditionJump(GateRef gate, bool flag); diff --git a/ecmascript/compiler/type_hcr_lowering.cpp b/ecmascript/compiler/type_hcr_lowering.cpp index 1c73a99c73e50c4e7d444ae6823fc390944ae0a8..3aa05ec9fd15451b709ae9118072a366f8f0132d 100644 --- a/ecmascript/compiler/type_hcr_lowering.cpp +++ b/ecmascript/compiler/type_hcr_lowering.cpp @@ -2182,11 +2182,44 @@ void TypeHCRLowering::LowerOrdinaryHasInstance(GateRef gate, GateRef glue) builder_.Bind(&objIsEcmaObject); { // 4. Let P be Get(C, "prototype"). - // target must be a builtin function - GateRef ctorHClass = builder_.LoadConstOffset(VariableType::JS_POINTER(), target, - JSFunction::PROTO_OR_DYNCLASS_OFFSET); - GateRef constructorPrototype = builder_.LoadPrototype(ctorHClass); + Label getCtorProtoSlowPath(&builder_); + Label ctorIsJSFunction(&builder_); + Label gotCtorPrototype(&builder_); + DEFVALUE(constructorPrototype, (&builder_), VariableType::JS_ANY(), builder_.Undefined()); + builder_.Branch(builder_.IsJSFunction(target), &ctorIsJSFunction, &getCtorProtoSlowPath); + builder_.Bind(&ctorIsJSFunction); + { + Label getCtorProtoFastPath(&builder_); + GateRef ctorProtoOrHC = builder_.LoadConstOffset(VariableType::JS_POINTER(), target, + JSFunction::PROTO_OR_DYNCLASS_OFFSET); + builder_.Branch(builder_.TaggedIsHole(ctorProtoOrHC), &getCtorProtoSlowPath, &getCtorProtoFastPath); + builder_.Bind(&getCtorProtoFastPath); + { + Label isHClass(&builder_); + Label isPrototype(&builder_); + builder_.Branch(builder_.IsJSHClass(ctorProtoOrHC), &isHClass, &isPrototype); + builder_.Bind(&isHClass); + { + constructorPrototype = builder_.LoadConstOffset(VariableType::JS_POINTER(), ctorProtoOrHC, + JSHClass::PROTOTYPE_OFFSET); + builder_.Jump(&gotCtorPrototype); + } + builder_.Bind(&isPrototype); + { + constructorPrototype = ctorProtoOrHC; + builder_.Jump(&gotCtorPrototype); + } + } + } + builder_.Bind(&getCtorProtoSlowPath); + { + auto prototypeString = builder_.GetGlobalConstantValue(ConstantIndex::PROTOTYPE_STRING_INDEX); + constructorPrototype = builder_.CallRuntime(glue, RTSTUB_ID(GetPropertyByName), Gate::InvalidGateRef, + { target, prototypeString }, gate); + builder_.Jump(&gotCtorPrototype); + } + builder_.Bind(&gotCtorPrototype); // 7. Repeat // a.Let O be O.[[GetPrototypeOf]](). // b.ReturnIfAbrupt(O). @@ -2203,7 +2236,7 @@ void TypeHCRLowering::LowerOrdinaryHasInstance(GateRef gate, GateRef glue) builder_.Branch(builder_.TaggedIsNull(*object), &afterLoop, &loopHead); builder_.LoopBegin(&loopHead); { - GateRef isEqual = builder_.Equal(*object, constructorPrototype); + GateRef isEqual = builder_.Equal(*object, *constructorPrototype); builder_.Branch(isEqual, &strictEqual1, ¬StrictEqual1); builder_.Bind(&strictEqual1); diff --git a/ecmascript/date_parse.h b/ecmascript/date_parse.h index 86b9fcba559e1aa56f8fdb9f4b5fb637ad696baf..99e5aab54bd6c5d1f3315d0b002e4a638dd47a2e 100644 --- a/ecmascript/date_parse.h +++ b/ecmascript/date_parse.h @@ -54,9 +54,9 @@ private: int index = 0; int num = 0; while (IsDigit()) { - // 9 : max decimal of int - if (index < 9) { - num = (value_ - '0') + num * JSDate::TEN; + int n = (value_ - '0') + num * JSDate::TEN; + if (n >= 0 && n < std::numeric_limits::max()) { + num = n; index++; } NextChar(); diff --git a/ecmascript/debugger/js_debugger_manager.h b/ecmascript/debugger/js_debugger_manager.h index 9bdf555dd2d8ccc24bb6f2c72e4b211a5bb78a80..68b1b173e912c295ac7a4d8ac2cd0d28eaef73ba 100644 --- a/ecmascript/debugger/js_debugger_manager.h +++ b/ecmascript/debugger/js_debugger_manager.h @@ -76,6 +76,16 @@ public: return isDebugMode_; } + void SetIsDebugApp(bool isDebugApp) + { + isDebugApp_ = isDebugApp; + } + + bool IsDebugApp() const + { + return isDebugApp_; + } + void SetMixedDebugEnabled(bool enabled) { isMixedDebugEnabled_ = enabled; @@ -174,6 +184,7 @@ public: private: bool isDebugMode_ {false}; + bool isDebugApp_ {false}; bool isMixedDebugEnabled_ { false }; ProtocolHandler *debuggerHandler_ {nullptr}; LibraryHandle debuggerLibraryHandle_ {nullptr}; diff --git a/ecmascript/dfx/cpu_profiler/cpu_profiler.cpp b/ecmascript/dfx/cpu_profiler/cpu_profiler.cpp index b90bace335a5346713ab16537f4912875e95a784..411db2efd1488410db2b3fe1df1e4cad8804f23c 100644 --- a/ecmascript/dfx/cpu_profiler/cpu_profiler.cpp +++ b/ecmascript/dfx/cpu_profiler/cpu_profiler.cpp @@ -21,7 +21,6 @@ #include #include "ecmascript/compiler/assembler/assembler.h" -#include "ecmascript/dfx/cpu_profiler/sampling_processor.h" #include "ecmascript/frames.h" #include "ecmascript/compiler/aot_file/aot_file_manager.h" #include "ecmascript/jspandafile/js_pandafile_manager.h" @@ -84,8 +83,8 @@ void CpuProfiler::StartCpuProfilerForInfo() generator_->SetTimeDeltaThreshold(interval_ * THRESHOLD_GROWTH_FACTORY + THRESHOLD_FIXED_INCREMENT); generator_->SetIsStart(true); - RunParams *params = new RunParams(generator_, static_cast(interval_), pthread_self()); - if (pthread_create(&tid_, nullptr, SamplingProcessor::Run, params) != 0) { + params_ = new RunParams(generator_, static_cast(interval_), pthread_self()); + if (pthread_create(&tid_, nullptr, SamplingProcessor::Run, params_) != 0) { LOG_ECMA(ERROR) << "pthread_create fail!"; return; } @@ -147,8 +146,8 @@ void CpuProfiler::StartCpuProfilerForFile(const std::string &fileName) generator_->SetTimeDeltaThreshold(interval_ * THRESHOLD_GROWTH_FACTORY + THRESHOLD_FIXED_INCREMENT); generator_->SetIsStart(true); - RunParams *params = new RunParams(generator_, static_cast(interval_), pthread_self()); - if (pthread_create(&tid_, nullptr, SamplingProcessor::Run, params) != 0) { + params_ = new RunParams(generator_, static_cast(interval_), pthread_self()); + if (pthread_create(&tid_, nullptr, SamplingProcessor::Run, params_) != 0) { LOG_ECMA(ERROR) << "pthread_create fail!"; return; } diff --git a/ecmascript/dfx/cpu_profiler/cpu_profiler.h b/ecmascript/dfx/cpu_profiler/cpu_profiler.h index c50153da8ce9febe7efbba59528695a354e7fcaf..c4653c251558d1e896c4b68211c31ced6fff60c8 100644 --- a/ecmascript/dfx/cpu_profiler/cpu_profiler.h +++ b/ecmascript/dfx/cpu_profiler/cpu_profiler.h @@ -19,6 +19,7 @@ #include #include "ecmascript/dfx/cpu_profiler/samples_record.h" +#include "ecmascript/dfx/cpu_profiler/sampling_processor.h" #include "ecmascript/interpreter/frame_handler.h" #include "ecmascript/js_thread.h" @@ -118,6 +119,7 @@ private: uint64_t beforeCallNapiTimeStamp_ = 0; std::atomic_bool isBuildNapiStack_ {false}; bool enableVMTag_ {false}; + RunParams *params_ = nullptr; }; class CallNapiScope { diff --git a/ecmascript/dfx/cpu_profiler/sampling_processor.cpp b/ecmascript/dfx/cpu_profiler/sampling_processor.cpp index 9d427b0b79ce85b09c543d3aa5d21b6a50dcdad3..288a3b453a991c98bf80d8eef8af2cf09fb187b6 100644 --- a/ecmascript/dfx/cpu_profiler/sampling_processor.cpp +++ b/ecmascript/dfx/cpu_profiler/sampling_processor.cpp @@ -71,7 +71,7 @@ void *SamplingProcessor::Run(void *arg) } } generator->SetThreadStopTime(); - pthread_setname_np(tid, "GC_WorkerThread"); + pthread_setname_np(tid, "OS_GC_WorkerThread"); if (generator->SemPost(1) != 0) { LOG_ECMA(ERROR) << "sem_[1] post failed"; return nullptr; diff --git a/ecmascript/dfx/stackinfo/js_stackinfo.cpp b/ecmascript/dfx/stackinfo/js_stackinfo.cpp index 593a1dc53c2769084f5fb3febf88f83b6cfa2299..966fcf46aa0e14895414f62fd4aea6e5520e814c 100644 --- a/ecmascript/dfx/stackinfo/js_stackinfo.cpp +++ b/ecmascript/dfx/stackinfo/js_stackinfo.cpp @@ -529,6 +529,21 @@ bool ArkGetMethodIdandJSPandaFileAddr(int pid, uintptr_t method, uintptr_t &meth return true; } +uint32_t ArkGetOffsetFromMethod(int pid, uintptr_t currentPtr, uintptr_t method) +{ + uintptr_t pc = 0; + if (!ReadUintptrFromAddr(pid, currentPtr, pc, true)) { + return 0; + } + uintptr_t byteCodeArrayAddr = method + Method::NATIVE_POINTER_OR_BYTECODE_ARRAY_OFFSET; + uintptr_t byteCodeArray = 0; + if (!ReadUintptrFromAddr(pid, byteCodeArrayAddr, byteCodeArray, true)) { + return 0; + } + uintptr_t offset = pc - byteCodeArray; + return static_cast(offset); +} + uint32_t ArkGetBytecodeOffset(int pid, uintptr_t method, uintptr_t frameType, uintptr_t currentPtr) { FrameType type = static_cast(frameType); @@ -537,33 +552,13 @@ uint32_t ArkGetBytecodeOffset(int pid, uintptr_t method, uintptr_t frameType, ui case FrameType::INTERPRETER_CONSTRUCTOR_FRAME: { currentPtr -= AsmInterpretedFrame::GetTypeOffset(); currentPtr += AsmInterpretedFrame::GetPcOffset(false); - uintptr_t pc = 0; - if (!ReadUintptrFromAddr(pid, currentPtr, pc, true)) { - return 0; - } - uintptr_t byteCodeArrayAddr = method + Method::NATIVE_POINTER_OR_BYTECODE_ARRAY_OFFSET; - uintptr_t byteCodeArray = 0; - if (!ReadUintptrFromAddr(pid, byteCodeArrayAddr, byteCodeArray, true)) { - return 0; - } - uintptr_t offset = pc - byteCodeArray; - return static_cast(offset); + return ArkGetOffsetFromMethod(pid, currentPtr, method); } case FrameType::INTERPRETER_FRAME: case FrameType::INTERPRETER_FAST_NEW_FRAME: { currentPtr -= InterpretedFrame::GetTypeOffset(); currentPtr += InterpretedFrame::GetPcOffset(false); - uintptr_t pc = 0; - if (!ReadUintptrFromAddr(pid, currentPtr, pc, true)) { - return 0; - } - uintptr_t byteCodeArrayAddr = method + Method::NATIVE_POINTER_OR_BYTECODE_ARRAY_OFFSET; - uintptr_t byteCodeArray = 0; - if (!ReadUintptrFromAddr(pid, byteCodeArrayAddr, byteCodeArray, true)) { - return 0; - } - uintptr_t offset = pc - byteCodeArray; - return static_cast(offset); + return ArkGetOffsetFromMethod(pid, currentPtr, method); } // ato need stackmaps case FrameType::OPTIMIZED_JS_FAST_CALL_FUNCTION_FRAME: @@ -598,22 +593,24 @@ bool ArkReadData(const std::string &hapPath, const std::string &sourceMapPath, c } *data = (char*)malloc(sizeof(char)*dataSize); if (memcpy_s(*data, dataSize, dataPtr.get(), dataSize) != EOK) { - LOG_COMPILER(FATAL) << "memcpy_s failed"; + LOG_ECMA(FATAL) << "memcpy_s failed"; UNREACHABLE(); } return true; } -std::string ArkGetHapName(int pid, std::string filename) +std::string ArkGetHapName(int pid, std::string &fileName) { - auto found = filename.find(JSPandaFile::BUNDLE_INSTALL_PATH); + auto found = fileName.find(JSPandaFile::BUNDLE_INSTALL_PATH); if (found == std::string::npos) { + LOG_ECMA(ERROR) << "ArkGetHapName can't find BUNDLE_INSTALL_PATH: " << fileName; return ""; } - std::string subPath = filename.substr(sizeof(JSPandaFile::BUNDLE_INSTALL_PATH) - 1); + std::string subPath = fileName.substr(sizeof(JSPandaFile::BUNDLE_INSTALL_PATH) - 1); auto endPos = subPath.find("/"); if (endPos == std::string::npos) { + LOG_ECMA(ERROR) << "ArkGetHapName can't find /: " << fileName; return ""; } @@ -623,7 +620,135 @@ std::string ArkGetHapName(int pid, std::string filename) return hapSource; } -bool GetArkNativeFrameInfo(int pid, uintptr_t *pc, uintptr_t *fp, uintptr_t *sp, panda::ecmascript::JsFrame *jsFrameList) +std::string ArkGetMapPath(std::string &fileName) +{ + auto lastSlash = fileName.rfind("/"); + if (lastSlash == std::string::npos) { + LOG_ECMA(ERROR) << "ArkGetMapPath can't find fisrt /: " << fileName; + return ""; + } + + auto secondLastSlash = fileName.rfind("/", lastSlash - 1); + if (secondLastSlash == std::string::npos) { + LOG_ECMA(ERROR) << "ArkGetMapPath can't second fisrt /: " << fileName; + return ""; + } + + std::string mapPath = fileName.substr(secondLastSlash + 1); + return mapPath; +} + +bool ArkIsNativeWithCallField(int pid, uintptr_t method) +{ + uintptr_t callFieldAddr = method + Method::CALL_FIELD_OFFSET; + uintptr_t callField = 0; + if (!ReadUintptrFromAddr(pid, callFieldAddr, callField, true)) { + return true; + } + return Method::IsNativeBit::Decode(callField); +} + +std::string ArkReadFileName(int pid, uintptr_t descAddr) +{ + std::string fileName; + while(true) { + uintptr_t desc = 0; + ReadUintptrFromAddr(pid, descAddr, desc, true); + bool key = 1; + size_t shiftAmount = 8; + for (size_t i = 0; i < sizeof(long); i++) { + char bottomEightBits = desc; + desc = desc >> shiftAmount; + if (!bottomEightBits) { + key = 0; + break; + } + fileName += bottomEightBits; + } + if (!key) { + break; + } + descAddr += sizeof(long); + } + return fileName; +} + +std::string ArkGetFileName(int pid, uintptr_t jsPandaFileAddr) +{ + size_t size = sizeof(JSPandaFile) / sizeof(long); + uintptr_t *jsPandaFilePart = new uintptr_t[size](); + for (size_t i = 0; i < size; i++) { + if (!ReadUintptrFromAddr(pid, jsPandaFileAddr, jsPandaFilePart[i], true)) { + LOG_ECMA(ERROR) << "ArkGetFileName failed, jsPandaFileAddr: " << jsPandaFileAddr; + return ""; + } + jsPandaFileAddr += sizeof(long); + } + JSPandaFile *jsPandaFile = reinterpret_cast(jsPandaFilePart); + uintptr_t descAddr = reinterpret_cast(const_cast(jsPandaFile->GetJSPandaFileDesc().c_str())); + delete []jsPandaFilePart; + return ArkReadFileName(pid, descAddr); +} + +JsFrame ArkParseJsFrameInfo(const panda_file::File *pf, std::string &fileName, + uintptr_t preMethodId, uintptr_t offset) +{ + std::shared_ptr newJsPandaFile = + JSPandaFileManager::GetInstance()->NewJSPandaFile(pf, fileName.c_str()); + auto debugExtractor = std::make_unique(newJsPandaFile.get()); + auto methodId = EntityId(preMethodId); + std::string name = MethodLiteral::ParseFunctionName(newJsPandaFile.get(), methodId); + name = name.size() ? name : "?"; + std::string url = debugExtractor->GetSourceFile(methodId); + + // line number and column number + int lineNumber = 0; + int columnNumber = 0; + auto callbackLineFunc = [&lineNumber](int32_t line) -> bool { + lineNumber = line + 1; + return true; + }; + auto callbackColumnFunc = [&columnNumber](int32_t column) -> bool { + columnNumber = column + 1; + return true; + }; + if (offset > 0) { + if (!debugExtractor->MatchLineWithOffset(callbackLineFunc, methodId, offset) || + !debugExtractor->MatchColumnWithOffset(callbackColumnFunc, methodId, offset)) { + lineNumber = 0; + columnNumber = 0; + } + } + JsFrame jsFrame; + size_t urlSize = url.size() + 1; + size_t nameSize = name.size() + 1; + if (strcpy_s(jsFrame.url, urlSize, url.c_str()) != EOK || + strcpy_s(jsFrame.functionName, nameSize, name.c_str()) != EOK) { + LOG_FULL(FATAL) << "strcpy_s failed"; + UNREACHABLE(); + } + jsFrame.line = lineNumber; + jsFrame.column = columnNumber; + return jsFrame; +} + +bool ArkGetNextFrame(uintptr_t ¤tPtr, uintptr_t typeOffset, + uintptr_t prevOffset, int pid) +{ + currentPtr -= typeOffset; + currentPtr += prevOffset; + if (!ReadUintptrFromAddr(pid, currentPtr, currentPtr, true)) { + return false; + } + if (currentPtr == 0) { + LOG_ECMA(ERROR) << "currentPtr is nullptr in GetArkNativeFrameInfo()!"; + return false; + } + return true; +} + +bool GetArkNativeFrameInfo(int pid, uintptr_t *pc, uintptr_t *fp, uintptr_t *sp, + size_t &size, panda::ecmascript::JsFrame **jsFrameList) { constexpr size_t FP_SIZE = 8; constexpr size_t LR_SIZE = 8; @@ -637,6 +762,7 @@ bool GetArkNativeFrameInfo(int pid, uintptr_t *pc, uintptr_t *fp, uintptr_t *sp, LOG_ECMA(ERROR) << "invalid pc in GetArkNativeFrameInfo()!"; return false; } + std::vector jsFrames; while (true) { currentPtr -= sizeof(FrameType); uintptr_t frameType = 0; @@ -655,100 +781,43 @@ bool GetArkNativeFrameInfo(int pid, uintptr_t *pc, uintptr_t *fp, uintptr_t *sp, break; } uintptr_t function = ArkGetFunction(pid, currentPtr, frameType); - if (function != 0) { - uintptr_t method = ArkCheckAndGetMethod(pid, function); - uintptr_t callFieldAddr = method + Method::CALL_FIELD_OFFSET; - uintptr_t callField = 0; - if (!ReadUintptrFromAddr(pid, callFieldAddr, callField, true)) { + if (!function) { + if (!ArkGetNextFrame(currentPtr, typeOffset, prevOffset, pid)) { return false; } - - bool isNativeWithCallField = Method::IsNativeBit::Decode(callField); - if (!isNativeWithCallField) { - uintptr_t jsPandaFileAddr = 0; - uintptr_t preMethodId = 0; - ArkGetMethodIdandJSPandaFileAddr(pid, method, preMethodId, jsPandaFileAddr); - uintptr_t offset = ArkGetBytecodeOffset(pid, method, frameType, currentPtr); - if (offset) { - size_t count = sizeof(JSPandaFile) / sizeof(long); - uintptr_t *jsPandaFilePart = new uintptr_t[count]; - for (size_t i = 0; i < count; i++) { - ReadUintptrFromAddr(pid, jsPandaFileAddr, jsPandaFilePart[i], true); - jsPandaFileAddr += sizeof(long); - } - JSPandaFile *jsPandaFile = reinterpret_cast(jsPandaFilePart); - uintptr_t desc = 0; - uintptr_t descAddr = reinterpret_cast(const_cast(jsPandaFile->GetJSPandaFileDesc().c_str())); - std::string fileName = {}; - while(true) { - ReadUintptrFromAddr(pid, descAddr, desc, true); - bool key = 1; - size_t shiftAmount = 8; - for (size_t i = 0; i < sizeof(long); i++) { - char bottomEightBits = desc; - desc = desc >> shiftAmount; - if (!bottomEightBits) { - key = 0; - break; - } - fileName += bottomEightBits; - } - if (!key) { - break; - } - descAddr += sizeof(long); - } - std::string hapPath = ArkGetHapName(pid, fileName); - std::string sourceMapPath = "ets/modules.abc"; - char *data = nullptr; - size_t dataSize = 0; - ArkReadData(hapPath, sourceMapPath, &data, dataSize); - auto pf = panda_file::OpenPandaFileFromMemory(data, dataSize); - std::shared_ptr newJsPandaFile = - JSPandaFileManager::GetInstance()->NewJSPandaFile(pf.get(), fileName.c_str()); - auto extractorPtr = std::make_unique(newJsPandaFile.get()); - DebugInfoExtractor *debugExtractor = extractorPtr.get(); - auto methodId = EntityId(preMethodId); - std::string name = MethodLiteral::ParseFunctionName(newJsPandaFile.get(), methodId); - name = name.size() ? name : "?"; - std::string url = debugExtractor->GetSourceFile(methodId); - // line number and column number - int lineNumber = 0; - int columnNumber = 0; - auto callbackLineFunc = [&lineNumber](int32_t line) -> bool { - lineNumber = line + 1; - return true; - }; - auto callbackColumnFunc = [&columnNumber](int32_t column) -> bool { - columnNumber = column + 1; - return true; - }; - if (!debugExtractor->MatchLineWithOffset(callbackLineFunc, methodId, offset) || - !debugExtractor->MatchColumnWithOffset(callbackColumnFunc, methodId, offset)) { - lineNumber = 0; - columnNumber = 0; - } - JsFrame jsFrame; - if (strcpy_s(jsFrame.url, url.size(), url.c_str()) != EOK && - strcpy_s(jsFrame.functionName, name.size(), name.c_str()) != EOK) { - LOG_FULL(FATAL) << "strcpy_s failed"; - UNREACHABLE(); - } - jsFrame.line = lineNumber; - jsFrame.column = columnNumber; - *jsFrameList++ = jsFrame; - delete []jsPandaFilePart; - free(data); - } + continue; + } + uintptr_t method = ArkCheckAndGetMethod(pid, function); + if (!method) { + if (!ArkGetNextFrame(currentPtr, typeOffset, prevOffset, pid)) { + return false; } + continue; } - currentPtr -= typeOffset; - currentPtr += prevOffset; - if (!ReadUintptrFromAddr(pid, currentPtr, currentPtr, true)) { - return false; + if (!ArkIsNativeWithCallField(pid, method)) { + uintptr_t jsPandaFileAddr = 0; + uintptr_t preMethodId = 0; + if (!ArkGetMethodIdandJSPandaFileAddr(pid, method, preMethodId, jsPandaFileAddr)) { + LOG_ECMA(ERROR) << "Method ERROR, method: " << method; + return false; + } + uintptr_t offset = ArkGetBytecodeOffset(pid, method, frameType, currentPtr); + std::string fileName = ArkGetFileName(pid, jsPandaFileAddr); + std::string hapPath = ArkGetHapName(pid, fileName); + std::string MapPath = ArkGetMapPath(fileName); + char *data = nullptr; + size_t dataSize = 0; + ArkReadData(hapPath, MapPath, &data, dataSize); + auto pf = panda_file::OpenPandaFileFromMemory(data, dataSize); + if (!pf.get()) { + LOG_ECMA(ERROR) << "OpenPandaFileFromMemory ERROR, hapPath: " << hapPath + << ", MapPath: " << MapPath; + return false; + } + JsFrame jsFrame = ArkParseJsFrameInfo(pf.get(), fileName, preMethodId, offset); + jsFrames.push_back(jsFrame); } - if (currentPtr == 0) { - LOG_ECMA(ERROR) << "currentPtr is nullptr in GetArkNativeFrameInfo()!"; + if (!ArkGetNextFrame(currentPtr, typeOffset, prevOffset, pid)) { return false; } } @@ -760,6 +829,17 @@ bool GetArkNativeFrameInfo(int pid, uintptr_t *pc, uintptr_t *fp, uintptr_t *sp, } currentPtr += LR_SIZE; *sp = currentPtr; + + size = jsFrames.size(); + if (!size) { + return true; + } + size_t jsFramesSize = sizeof(JsFrame) * size; + *jsFrameList = (JsFrame*)malloc(jsFramesSize); + if (memcpy_s(*jsFrameList, jsFramesSize, jsFrames.data(), jsFramesSize) != EOK) { + LOG_ECMA(FATAL) << "JsFrame memcpy_s failed, jsFramesSize: " << jsFramesSize; + UNREACHABLE(); + } return true; } #endif @@ -937,10 +1017,10 @@ __attribute__((visibility("default"))) int get_ark_js_heap_crash_info( } #if defined(PANDA_TARGET_OHOS) -__attribute__((visibility("default"))) int get_ark_native_frame_info( - int pid, uintptr_t *pc, uintptr_t *fp, uintptr_t *sp, panda::ecmascript::JsFrame *jsFrameList) +__attribute__((visibility("default"))) int get_ark_native_frame_info(int pid, uintptr_t *pc, + uintptr_t *fp, uintptr_t *sp, size_t &size, panda::ecmascript::JsFrame **jsFrameList) { - if (GetArkNativeFrameInfo(pid, pc, fp, sp, jsFrameList)) { + if (GetArkNativeFrameInfo(pid, pc, fp, sp, size, jsFrameList)) { return 1; } return -1; diff --git a/ecmascript/dfx/stackinfo/js_stackinfo.h b/ecmascript/dfx/stackinfo/js_stackinfo.h index a7fac64e9e9f8a6352feefad5bd4fb97228d2965..73421d8280663623ca87902774c6184ed41fe132 100644 --- a/ecmascript/dfx/stackinfo/js_stackinfo.h +++ b/ecmascript/dfx/stackinfo/js_stackinfo.h @@ -65,7 +65,7 @@ extern "C" int get_ark_js_heap_crash_info( int pid, uintptr_t *x20, uintptr_t *fp, int out_js_info, char *buf, size_t buf_sz); #if defined(PANDA_TARGET_OHOS) extern "C" int get_ark_native_frame_info( - int pid, uintptr_t *pc, uintptr_t *fp, uintptr_t *sp, panda::ecmascript::JsFrame *jsFrameList); + int pid, uintptr_t *pc, uintptr_t *fp, uintptr_t *sp, size_t &size, panda::ecmascript::JsFrame **jsFrameList); #endif // define in dfx_signal_handler.h typedef void(*ThreadInfoCallback)(char *buf, size_t len, void *ucontext); diff --git a/ecmascript/ecma_context.cpp b/ecmascript/ecma_context.cpp index f2ab099bd832ab1f5a5b14e7569c9a92570a0ca3..5b75361b40a4702d3743ec4fdd90beb2bd6b5584 100644 --- a/ecmascript/ecma_context.cpp +++ b/ecmascript/ecma_context.cpp @@ -42,6 +42,7 @@ #include "ecmascript/require/js_require_manager.h" #include "ecmascript/snapshot/mem/snapshot.h" #include "ecmascript/platform/log.h" +#include "ecmascript/global_index_map.h" namespace panda::ecmascript { using PathHelper = base::PathHelper; @@ -748,6 +749,8 @@ void EcmaContext::Iterate(const RootVisitor &v, const RootRangeVisitor &rv) v(Root::ROOT_VM, ObjectSlot(reinterpret_cast(&numberToStringResultCache_))); v(Root::ROOT_VM, ObjectSlot(reinterpret_cast(&stringSplitResultCache_))); v(Root::ROOT_VM, ObjectSlot(reinterpret_cast(µJobQueue_))); + v(Root::ROOT_VM, ObjectSlot(reinterpret_cast(&pointerToIndexDictionary_))); + if (moduleManager_) { moduleManager_->Iterate(v); } diff --git a/ecmascript/ecma_context.h b/ecmascript/ecma_context.h index b2804ce55f57b62e585382dc69db69dd2601e589..faa01aa88338ff9a55162ae0955c4e5518cf16cc 100644 --- a/ecmascript/ecma_context.h +++ b/ecmascript/ecma_context.h @@ -47,6 +47,7 @@ class ConstantPool; class JSPromise; class RegExpExecResultCache; class EcmaHandleScope; +class GlobalIndexMap; enum class PromiseRejectionEvent : uint8_t; template @@ -516,6 +517,7 @@ private: JSTaggedValue numberToStringResultCache_ {JSTaggedValue::Hole()}; JSTaggedValue stringSplitResultCache_ {JSTaggedValue::Hole()}; JSTaggedValue globalEnv_ {JSTaggedValue::Hole()}; + JSTaggedValue pointerToIndexDictionary_ {JSTaggedValue::Hole()}; JSTaggedValue regexpCache_ {JSTaggedValue::Hole()}; JSTaggedValue regexpGlobal_ {JSTaggedValue::Hole()}; JSTaggedValue microJobQueue_ {JSTaggedValue::Hole()}; @@ -587,6 +589,7 @@ private: friend class ObjectFactory; friend class panda::JSNApi; friend class AOTFileManager; + friend class GlobalIndexMap; }; } // namespace ecmascript } // namespace panda diff --git a/ecmascript/ecma_string-inl.h b/ecmascript/ecma_string-inl.h index c1d82ca8e9979bf718e9f793f584e15c15444eb1..2a255834af95d3c372d3bc56d870612f89b7b89a 100644 --- a/ecmascript/ecma_string-inl.h +++ b/ecmascript/ecma_string-inl.h @@ -68,6 +68,35 @@ inline EcmaString *EcmaString::CreateFromUtf8(const EcmaVM *vm, const uint8_t *u return string; } +inline EcmaString *EcmaString::CreateUtf16StringFromUtf8(const EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf16Len, + MemSpaceType type) +{ + if (utf16Len == 0) { + return vm->GetFactory()->GetEmptyString().GetObject(); + } + auto string = CreateLineStringWithSpaceType(vm, utf16Len, false, type); + ASSERT(string != nullptr); + auto len = utf::ConvertRegionMUtf8ToUtf16( + utf8Data, string->GetDataUtf16Writable(), utf::Mutf8Size(utf8Data), utf16Len, 0); + if (len < utf16Len) { + string->TrimLineString(vm->GetJSThread(), len); + } + ASSERT_PRINT(false == CanBeCompressed(string), "Bad input canBeCompress!"); + return string; +} + +inline void EcmaString::TrimLineString(const JSThread *thread, uint32_t newLength) +{ + ASSERT(IsLineString()); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + uint32_t oldLength = GetLength(); + ASSERT(oldLength > newLength); + size_t trimBytes = (oldLength - newLength) * (IsUtf8() ? sizeof(uint8_t) : sizeof(uint16_t)); + size_t size = IsUtf8() ? LineEcmaString::ComputeSizeUtf8(newLength) : LineEcmaString::ComputeSizeUtf16(newLength); + factory->FillFreeObject(ToUintPtr(this) + size, trimBytes, RemoveSlots::YES, ToUintPtr(this)); + SetLength(newLength, CanBeCompressed(this)); +} + inline EcmaString *EcmaString::CreateFromUtf16(const EcmaVM *vm, const uint16_t *utf16Data, uint32_t utf16Len, bool canBeCompress, MemSpaceType type) { diff --git a/ecmascript/ecma_string.h b/ecmascript/ecma_string.h index 54d017110c965c67a3bfa5b5689f0613d2807d2f..9f5ade0e900a2e069c9791ab1e69e80801055fe5 100644 --- a/ecmascript/ecma_string.h +++ b/ecmascript/ecma_string.h @@ -103,6 +103,8 @@ private: static EcmaString *CreateFromUtf8(const EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf8Len, bool canBeCompress, MemSpaceType type = MemSpaceType::SEMI_SPACE, bool isConstantString = false, uint32_t idOffset = 0); + static EcmaString *CreateUtf16StringFromUtf8(const EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf8Len, + MemSpaceType type = MemSpaceType::SEMI_SPACE); static EcmaString *CreateFromUtf16(const EcmaVM *vm, const uint16_t *utf16Data, uint32_t utf16Len, bool canBeCompress, MemSpaceType type = MemSpaceType::SEMI_SPACE); static SlicedString *CreateSlicedString(const EcmaVM *vm, MemSpaceType type = MemSpaceType::SEMI_SPACE); @@ -132,7 +134,7 @@ private: // not change src data structure static inline EcmaString *FastSubUtf16String(const EcmaVM *vm, const JSHandle &src, uint32_t start, uint32_t length); - + inline void TrimLineString(const JSThread *thread, uint32_t newLength); inline bool IsUtf8() const { return (GetMixLength() & STRING_COMPRESSED_BIT) == STRING_COMPRESSED; @@ -1030,6 +1032,12 @@ public: return EcmaString::CreateConstantString(vm, utf8Data, length, compressed, type, idOffset); } + static EcmaString *CreateUtf16StringFromUtf8(const EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf8Len, + MemSpaceType type = MemSpaceType::SEMI_SPACE) + { + return EcmaString::CreateUtf16StringFromUtf8(vm, utf8Data, utf8Len, type); + } + static EcmaString *CreateFromUtf16(const EcmaVM *vm, const uint16_t *utf16Data, uint32_t utf16Len, bool canBeCompress, MemSpaceType type = MemSpaceType::SEMI_SPACE) { diff --git a/ecmascript/ecma_string_table.cpp b/ecmascript/ecma_string_table.cpp index a468b4ceef575bb77400594cf2c2a5ae6f5cad56..35750327d8e5bdf70895c8d02ddc2aa4baf7c114 100644 --- a/ecmascript/ecma_string_table.cpp +++ b/ecmascript/ecma_string_table.cpp @@ -25,8 +25,8 @@ namespace panda::ecmascript { EcmaStringTable::EcmaStringTable(const EcmaVM *vm) : vm_(vm) {} -EcmaString *EcmaStringTable::GetString(const JSHandle &firstString, - const JSHandle &secondString) const +std::pair EcmaStringTable::GetString(const JSHandle &firstString, + const JSHandle &secondString) const { ASSERT(EcmaStringAccessor(firstString).NotTreeString()); ASSERT(EcmaStringAccessor(secondString).NotTreeString()); @@ -37,41 +37,41 @@ EcmaString *EcmaStringTable::GetString(const JSHandle &firstString, for (auto item = range.first; item != range.second; ++item) { auto foundString = item->second; if (EcmaStringAccessor(foundString).EqualToSplicedString(*firstString, *secondString)) { - return foundString; + return std::make_pair(foundString, hashCode); } } - return nullptr; + return std::make_pair(nullptr, hashCode); } -EcmaString *EcmaStringTable::GetString(const uint8_t *utf8Data, uint32_t utf8Len, bool canBeCompress) const +std::pair EcmaStringTable::GetString(const uint8_t *utf8Data, + uint32_t utf8Len, bool canBeCompress) const { uint32_t hashCode = EcmaStringAccessor::ComputeHashcodeUtf8(utf8Data, utf8Len, canBeCompress); auto range = table_.equal_range(hashCode); for (auto item = range.first; item != range.second; ++item) { auto foundString = item->second; if (EcmaStringAccessor::StringIsEqualUint8Data(foundString, utf8Data, utf8Len, canBeCompress)) { - return foundString; + return std::make_pair(foundString, hashCode); } } - return nullptr; + return std::make_pair(nullptr, hashCode); } -EcmaString *EcmaStringTable::GetString(const uint16_t *utf16Data, uint32_t utf16Len) const +std::pair EcmaStringTable::GetString(const uint16_t *utf16Data, uint32_t utf16Len) const { uint32_t hashCode = EcmaStringAccessor::ComputeHashcodeUtf16(const_cast(utf16Data), utf16Len); auto range = table_.equal_range(hashCode); for (auto item = range.first; item != range.second; ++item) { auto foundString = item->second; if (EcmaStringAccessor::StringsAreEqualUtf16(foundString, utf16Data, utf16Len)) { - return foundString; + return std::make_pair(foundString, hashCode); } } - return nullptr; + return std::make_pair(nullptr, hashCode); } EcmaString *EcmaStringTable::GetString(EcmaString *string) const { - ASSERT(EcmaStringAccessor(string).NotTreeString()); auto hashcode = EcmaStringAccessor(string).GetHashcode(); auto range = table_.equal_range(hashcode); for (auto item = range.first; item != range.second; ++item) { @@ -106,27 +106,30 @@ EcmaString *EcmaStringTable::GetOrInternString(const JSHandle &first { auto firstFlat = JSHandle(vm_->GetJSThread(), EcmaStringAccessor::Flatten(vm_, firstString)); auto secondFlat = JSHandle(vm_->GetJSThread(), EcmaStringAccessor::Flatten(vm_, secondString)); - EcmaString *concatString = GetString(firstFlat, secondFlat); - if (concatString != nullptr) { - return concatString; + std::pair result = GetString(firstFlat, secondFlat); + if (result.first != nullptr) { + return result.first; } JSHandle concatHandle(vm_->GetJSThread(), EcmaStringAccessor::Concat(vm_, firstFlat, secondFlat, MemSpaceType::OLD_SPACE)); - concatString = EcmaStringAccessor::Flatten(vm_, concatHandle, MemSpaceType::OLD_SPACE); + EcmaString *concatString = EcmaStringAccessor::Flatten(vm_, concatHandle, MemSpaceType::OLD_SPACE); + concatString->SetMixHashcode(result.second); InternString(concatString); return concatString; } EcmaString *EcmaStringTable::GetOrInternString(const uint8_t *utf8Data, uint32_t utf8Len, bool canBeCompress) { - EcmaString *result = GetString(utf8Data, utf8Len, canBeCompress); - if (result != nullptr) { - return result; + std::pair result = GetString(utf8Data, utf8Len, canBeCompress); + if (result.first != nullptr) { + return result.first; } - result = EcmaStringAccessor::CreateFromUtf8(vm_, utf8Data, utf8Len, canBeCompress, MemSpaceType::OLD_SPACE); - InternString(result); - return result; + EcmaString *str = + EcmaStringAccessor::CreateFromUtf8(vm_, utf8Data, utf8Len, canBeCompress, MemSpaceType::OLD_SPACE); + str->SetMixHashcode(result.second); + InternString(str); + return str; } /* @@ -135,26 +138,29 @@ EcmaString *EcmaStringTable::GetOrInternString(const uint8_t *utf8Data, uint32_t */ EcmaString *EcmaStringTable::CreateAndInternStringNonMovable(const uint8_t *utf8Data, uint32_t utf8Len) { - EcmaString *result = GetString(utf8Data, utf8Len, true); - if (result != nullptr) { - return result; + std::pair result = GetString(utf8Data, utf8Len, true); + if (result.first != nullptr) { + return result.first; } - result = EcmaStringAccessor::CreateFromUtf8(vm_, utf8Data, utf8Len, true, MemSpaceType::NON_MOVABLE); - InternString(result); - return result; + EcmaString *str = EcmaStringAccessor::CreateFromUtf8(vm_, utf8Data, utf8Len, true, MemSpaceType::NON_MOVABLE); + str->SetMixHashcode(result.second); + InternString(str); + return str; } EcmaString *EcmaStringTable::GetOrInternString(const uint16_t *utf16Data, uint32_t utf16Len, bool canBeCompress) { - EcmaString *result = GetString(utf16Data, utf16Len); - if (result != nullptr) { - return result; + std::pair result = GetString(utf16Data, utf16Len); + if (result.first != nullptr) { + return result.first; } - result = EcmaStringAccessor::CreateFromUtf16(vm_, utf16Data, utf16Len, canBeCompress, MemSpaceType::OLD_SPACE); - InternString(result); - return result; + EcmaString *str = + EcmaStringAccessor::CreateFromUtf16(vm_, utf16Data, utf16Len, canBeCompress, MemSpaceType::OLD_SPACE); + str->SetMixHashcode(result.second); + InternString(str); + return str; } EcmaString *EcmaStringTable::GetOrInternString(EcmaString *string) @@ -194,33 +200,35 @@ EcmaString *EcmaStringTable::GetOrInternStringWithSpaceType(const uint8_t *utf8D bool canBeCompress, MemSpaceType type, bool isConstantString, uint32_t idOffset) { - EcmaString *result = GetString(utf8Data, utf8Len, canBeCompress); - if (result != nullptr) { - return result; + std::pair result = GetString(utf8Data, utf8Len, canBeCompress); + if (result.first != nullptr) { + return result.first; } type = type == MemSpaceType::NON_MOVABLE ? MemSpaceType::NON_MOVABLE : MemSpaceType::OLD_SPACE; + EcmaString *str; if (canBeCompress) { // Constant string will be created in this branch. - result = EcmaStringAccessor::CreateFromUtf8(vm_, utf8Data, utf8Len, canBeCompress, type, isConstantString, + str = EcmaStringAccessor::CreateFromUtf8(vm_, utf8Data, utf8Len, canBeCompress, type, isConstantString, idOffset); } else { - result = EcmaStringAccessor::CreateFromUtf8(vm_, utf8Data, utf8Len, canBeCompress, type); + str = EcmaStringAccessor::CreateFromUtf8(vm_, utf8Data, utf8Len, canBeCompress, type); } - InternString(result); - return result; + str->SetMixHashcode(result.second); + InternString(str); + return str; } -EcmaString *EcmaStringTable::GetOrInternStringWithSpaceType(const uint16_t *utf16Data, uint32_t utf16Len, - bool canBeCompress, MemSpaceType type) +EcmaString *EcmaStringTable::GetOrInternStringWithSpaceType(const uint8_t *utf8Data, uint32_t utf16Len, + MemSpaceType type) { - EcmaString *result = GetString(utf16Data, utf16Len); + type = type == MemSpaceType::NON_MOVABLE ? MemSpaceType::NON_MOVABLE : MemSpaceType::OLD_SPACE; + EcmaString *str = EcmaStringAccessor::CreateUtf16StringFromUtf8(vm_, utf8Data, utf16Len, type); + EcmaString *result = GetString(str); if (result != nullptr) { return result; } - type = type == MemSpaceType::NON_MOVABLE ? MemSpaceType::NON_MOVABLE : MemSpaceType::OLD_SPACE; - result = EcmaStringAccessor::CreateFromUtf16(vm_, utf16Data, utf16Len, canBeCompress, type); - InternString(result); - return result; + InternString(str); + return str; } void EcmaStringTable::SweepWeakReference(const WeakRootVisitor &visitor) diff --git a/ecmascript/ecma_string_table.h b/ecmascript/ecma_string_table.h index 3feae6ab77b33e2aa76e284cebcc4b15fb7802af..2d3ae10c16ce8d41a6d01cc611694a4b523734dc 100644 --- a/ecmascript/ecma_string_table.h +++ b/ecmascript/ecma_string_table.h @@ -41,7 +41,7 @@ public: EcmaString *GetOrInternString(EcmaString *string); EcmaString *GetOrInternStringWithSpaceType(const uint8_t *utf8Data, uint32_t utf8Len, bool canBeCompress, MemSpaceType type, bool isConstantString, uint32_t idOffset); - EcmaString *GetOrInternStringWithSpaceType(const uint16_t *utf16Data, uint32_t utf16Len, bool canBeCompress, + EcmaString *GetOrInternStringWithSpaceType(const uint8_t *utf8Data, uint32_t utf16Len, MemSpaceType type); EcmaString *TryGetInternString(EcmaString *string); @@ -52,9 +52,10 @@ private: NO_COPY_SEMANTIC(EcmaStringTable); NO_MOVE_SEMANTIC(EcmaStringTable); - EcmaString *GetString(const JSHandle &firstString, const JSHandle &secondString) const; - EcmaString *GetString(const uint8_t *utf8Data, uint32_t utf8Len, bool canBeCompress) const; - EcmaString *GetString(const uint16_t *utf16Data, uint32_t utf16Len) const; + std::pair GetString( + const JSHandle &firstString, const JSHandle &secondString) const; + std::pair GetString(const uint8_t *utf8Data, uint32_t utf8Len, bool canBeCompress) const; + std::pair GetString(const uint16_t *utf16Data, uint32_t utf16Len) const; EcmaString *GetString(EcmaString *string) const; void InternString(EcmaString *string); diff --git a/ecmascript/extractortool/src/zip_file.cpp b/ecmascript/extractortool/src/zip_file.cpp index 58648a9b78f30473a4be8f6471117db088f58522..df8d9b2e3dafaa1b64a2d3e67712d620a02dba40 100644 --- a/ecmascript/extractortool/src/zip_file.cpp +++ b/ecmascript/extractortool/src/zip_file.cpp @@ -186,14 +186,8 @@ bool ZipFile::Open() if (pathName_.length() > PATH_MAX) { return false; } - std::string realPath; - realPath.reserve(PATH_MAX); - realPath.resize(PATH_MAX - 1); - if (realpath(pathName_.c_str(), &(realPath[0])) == nullptr) { - return false; - } - zipFileReader_ = ZipFileReader::CreateZipFileReader(realPath); + zipFileReader_ = ZipFileReader::CreateZipFileReader(pathName_); if (!zipFileReader_) { return false; } diff --git a/ecmascript/extractortool/src/zip_file_reader.cpp b/ecmascript/extractortool/src/zip_file_reader.cpp index 6b0f1aae52394423bbd8231e58ce5a33f23977d8..0b94b8d39d2f8e5ea19e806c93bba60e205f6de5 100644 --- a/ecmascript/extractortool/src/zip_file_reader.cpp +++ b/ecmascript/extractortool/src/zip_file_reader.cpp @@ -76,11 +76,7 @@ bool ZipFileReader::init() if (filePath_.empty()) { return false; } - char resolvePath[PATH_MAX] = {0}; - if (realpath(filePath_.c_str(), resolvePath) == nullptr) { - return false; - } - fd_ = open(resolvePath, O_RDONLY); + fd_ = open(filePath_.c_str(), O_RDONLY); if (fd_ < 0) { return false; } diff --git a/ecmascript/global_index_map.cpp b/ecmascript/global_index_map.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ab4cde05a9d818f4cb6ba1ecf800c73213f3d9e1 --- /dev/null +++ b/ecmascript/global_index_map.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/global_env.h" +#include "ecmascript/global_index_map.h" + +namespace panda::ecmascript { +void GlobalIndexMap::Initialize(const JSThread *thread, + JSMutableHandle globalIndexMap) +{ + // GlobalIndex map should be created when it is first used. + InitGlobalIndexMap(thread, globalIndexMap); + + // Init GlobalIndex map + InitGlobalConst(thread, globalIndexMap); + InitGlobalEnv(thread, globalIndexMap); + InitBuiltinEntries(thread, globalIndexMap); +} + +void GlobalIndexMap::InitGlobalIndexMap(const JSThread *thread, + JSMutableHandle globalIndexMap) +{ + if (globalIndexMap.GetTaggedValue().IsHeapObject()) { + return ; + } + globalIndexMap.Update(PointerToIndexDictionary::Create(thread)); +} + +void GlobalIndexMap::InitGlobalConst(const JSThread *thread, + JSMutableHandle globalIndexMap) +{ + ASSERT_PRINT(globalIndexMap.GetTaggedValue().IsHeapObject(), "Global's IndexMap is not existed."); + auto globalConst = const_cast(thread->GlobalConstants()); + uint32_t constantCount = globalConst->GetConstantCount(); + JSMutableHandle globalIndexMapHandle(thread, globalIndexMap); + JSMutableHandle keyHandle(thread, JSTaggedValue::Undefined()); + JSMutableHandle valueHandle(thread, JSTaggedValue::Undefined()); + for (uint32_t index = 0; index < constantCount; index++) { + JSTaggedValue objectValue = globalConst->GetGlobalConstantObject(index); + if (objectValue.IsHeapObject() && !objectValue.IsString()) { + keyHandle.Update(objectValue); + + GlobalIndex globalIndex; + globalIndex.UpdateGlobalConstId(index); + valueHandle.Update(JSTaggedValue(globalIndex.GetGlobalIndex())); + JSHandle newDict = + PointerToIndexDictionary::PutIfAbsent(thread, globalIndexMapHandle, keyHandle, valueHandle); + globalIndexMapHandle.Update(newDict); + } + } + globalIndexMap.Update(globalIndexMapHandle); +} + +void GlobalIndexMap::InitGlobalEnv(const JSThread *thread, + JSMutableHandle globalIndexMap) +{ + ASSERT_PRINT(globalIndexMap.GetTaggedValue().IsHeapObject(), "Global's IndexMap is not existed."); + auto globalEnv = thread->GetEcmaVM()->GetGlobalEnv(); + uint32_t globalEnvFieldSize = globalEnv->GetGlobalEnvFieldSize(); + JSMutableHandle globalIndexMapHandle(thread, globalIndexMap); + JSMutableHandle keyHandle(thread, JSTaggedValue::Undefined()); + JSMutableHandle valueHandle(thread, JSTaggedValue::Undefined()); + for (uint32_t index = 0; index < globalEnvFieldSize; index++) { + JSTaggedValue objectValue = globalEnv->GetGlobalEnvObjectByIndex(index).GetTaggedValue(); + if (objectValue.IsHeapObject()) { + keyHandle.Update(objectValue); + + GlobalIndex globalIndex; + globalIndex.UpdateGlobalEnvId(index); + valueHandle.Update(JSTaggedValue(globalIndex.GetGlobalIndex())); + JSHandle newDict = + PointerToIndexDictionary::PutIfAbsent(thread, globalIndexMapHandle, keyHandle, valueHandle); + globalIndexMapHandle.Update(newDict); + } + } + globalIndexMap.Update(globalIndexMapHandle); +} + +void GlobalIndexMap::InitBuiltinEntries(const JSThread *thread, + JSMutableHandle globalIndexMap) +{ + ASSERT_PRINT(globalIndexMap.GetTaggedValue().IsHeapObject(), "Global's IndexMap is not existed."); + auto builtinEntries = thread->GetBuiltinEntries(); + uint32_t builtinEntriesCount = BuiltinEntries::COUNT; + JSMutableHandle globalIndexMapHandle(thread, globalIndexMap); + JSMutableHandle keyHandle(thread, JSTaggedValue::Undefined()); + JSMutableHandle valueHandle(thread, JSTaggedValue::Undefined()); + for (uint32_t index = 0; index < builtinEntriesCount; index++) { + JSTaggedValue objectValue = builtinEntries.builtin_[index].hClass_; + keyHandle.Update(objectValue); + if (objectValue.IsHeapObject()) { + GlobalIndex globalIndex; + globalIndex.UpdateBuiltinEntriesId(index); + valueHandle.Update(JSTaggedValue(globalIndex.GetGlobalIndex())); + JSHandle newDict = + PointerToIndexDictionary::PutIfAbsent(thread, globalIndexMapHandle, keyHandle, valueHandle); + globalIndexMapHandle.Update(newDict); + } + } + globalIndexMap.Update(globalIndexMapHandle); +} + +void GlobalIndexMap::FindGlobalIndex(JSHandle globalIndexMap, + JSTaggedValue objAddress, GlobalIndex *globalIndex) +{ + int entry = globalIndexMap->FindEntry(objAddress); + if (entry != -1) { + *globalIndex = GlobalIndex(globalIndexMap->GetValue(entry).GetInt()); + } +} +} // namespace panda::ecmascript diff --git a/ecmascript/global_index_map.h b/ecmascript/global_index_map.h new file mode 100644 index 0000000000000000000000000000000000000000..116796351d8d8597ff802f2fa1f6db821c383099 --- /dev/null +++ b/ecmascript/global_index_map.h @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2023 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_GLOBAL_INDEX_MAP_H +#define ECMASCRIPT_GLOBAL_INDEX_MAP_H + +#include "ecmascript/tagged_dictionary.h" +#include "utils/bit_field.h" + +namespace panda::ecmascript { +class GlobalIndex { +public: + static constexpr uint32_t GLOBAL_CONST_BITFILED_NUM = 10; + static constexpr uint32_t GLOBAL_ENV_BITFILED_NUM = 10; + static constexpr uint32_t BUILTIN_ENTRIES_BITFILED_NUM = 10; + using GlobalConstBits = BitField; + using GlobalEnvBits = GlobalConstBits::NextField; + using BuiltinEntriesBits = GlobalEnvBits::NextField; + + GlobalIndex() = default; + explicit GlobalIndex(uint32_t index) : index_(index) {} + explicit GlobalIndex(int32_t index) : index_(static_cast(index)) {} + + int GetGlobalConstId() const + { + int id = GlobalConstBits::Decode(index_); + return id - 1; + } + + void UpdateGlobalConstId(size_t id) + { + index_ = GlobalConstBits::Update(index_, id + 1); + } + + bool IsGlobalConstId() const + { + return GlobalConstBits::Decode(index_); + } + + int GetGlobalEnvId() const + { + int id = GlobalEnvBits::Decode(index_); + return id - 1; + } + + void UpdateGlobalEnvId(size_t id) + { + index_ = GlobalEnvBits::Update(index_, id + 1); + } + + bool IsGlobalEnvId() const + { + return GlobalEnvBits::Decode(index_); + } + + int GetBuiltinEntriesId() const + { + int id = BuiltinEntriesBits::Decode(index_); + return id - 1; + } + + void UpdateBuiltinEntriesId(size_t id) + { + index_ = BuiltinEntriesBits::Update(index_, id + 1); + } + + bool IsBuiltinEntriesId() const + { + return BuiltinEntriesBits::Decode(index_); + } + + uint32_t GetGlobalIndex() const + { + return index_; + } + + void UpdateGlobalIndex(size_t index) + { + index_ = index; + } + + bool operator!=(const GlobalIndex &right) const + { + return index_ != right.index_; + } + + bool operator==(const GlobalIndex &right) const + { + return index_ == right.index_; + } + +private: + uint32_t index_ {0}; +}; + +// HeapObject's Pointer To IndexHashmap +class GlobalIndexMap { +public: + static void Initialize(const JSThread *thread, + JSMutableHandle globalIndexMap); + static void InitGlobalIndexMap(const JSThread *thread, + JSMutableHandle globalIndexMap); + static void InitGlobalConst(const JSThread *thread, + JSMutableHandle globalIndexMap); + static void InitGlobalEnv(const JSThread *thread, + JSMutableHandle globalIndexMap); + static void InitBuiltinEntries(const JSThread *thread, + JSMutableHandle globalIndexMap); + static void FindGlobalIndex(JSHandle globalIndexMap, + JSTaggedValue obj, GlobalIndex *globalIndex); + static inline JSHandle GetGlobalIndexMap(EcmaContext *context) + { + return JSHandle(reinterpret_cast(&context->pointerToIndexDictionary_)); + } +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_GLOBAL_INDEX_MAP_H \ No newline at end of file diff --git a/ecmascript/js_array.cpp b/ecmascript/js_array.cpp index 6ef5c2bf9bda005fe6202b3dd9402294e142a8f6..87f951d481b6d71f86e443bbf9f10b0947f53a40 100644 --- a/ecmascript/js_array.cpp +++ b/ecmascript/js_array.cpp @@ -115,6 +115,10 @@ JSTaggedValue JSArray::ArraySpeciesCreate(JSThread *thread, const JSHandle constructor(thread, JSTaggedValue::Undefined()); if (isArray) { // Let C be Get(originalArray, "constructor"). + auto *hclass = originalArray->GetJSHClass(); + if (hclass->IsJSArray() && !hclass->HasConstructor()) { + return JSArray::ArrayCreate(thread, length, ArrayMode::LITERAL).GetTaggedValue(); + } JSHandle constructorKey = globalConst->GetHandledConstructorString(); constructor = JSTaggedValue::GetProperty(thread, originalValue, constructorKey).GetValue(); // ReturnIfAbrupt(C). diff --git a/ecmascript/js_array.h b/ecmascript/js_array.h index 6c4b9e09c94601c2659c95ee39de4456430d9196..ad5c94e7bbe478622a2abd2e78b1ca837f1cb6d0 100644 --- a/ecmascript/js_array.h +++ b/ecmascript/js_array.h @@ -57,6 +57,16 @@ public: SetLength(length); } + inline uint32_t GetHintLength() const + { + auto trackInfo = GetTrackInfo(); + if (trackInfo.IsInt()) { + int hint = trackInfo.GetInt(); + return hint > 0 ? hint : 0; + } + return 0; + } + static constexpr size_t LENGTH_OFFSET = JSObject::SIZE; ACCESSORS_PRIMITIVE_FIELD(Length, uint32_t, LENGTH_OFFSET, TRACE_INDEX_OFFSET) ACCESSORS_PRIMITIVE_FIELD(TraceIndex, uint32_t, TRACE_INDEX_OFFSET, TRACK_INFO_OFFSET) diff --git a/ecmascript/js_handle.h b/ecmascript/js_handle.h index a07caf22627187fb3d7c14a4f8814cebf3221697..81fcee1efd5c4f0d77500e714489e75d62754239 100644 --- a/ecmascript/js_handle.h +++ b/ecmascript/js_handle.h @@ -156,7 +156,9 @@ public: { if (!std::is_convertible::value) { ASSERT(slot != 0); - T::Cast((*reinterpret_cast(slot)).GetTaggedObject()); + if ((*reinterpret_cast(slot)).IsHeapObject()) { + T::Cast((*reinterpret_cast(slot)).GetTaggedObject()); + } } } diff --git a/ecmascript/js_object-inl.h b/ecmascript/js_object-inl.h index 98876e1b50daacb75183ea5b0d0deafc1805b1e6..4089ef5d9903b454deb5b621ed48fb0b274f3456 100644 --- a/ecmascript/js_object-inl.h +++ b/ecmascript/js_object-inl.h @@ -24,6 +24,7 @@ #include "ecmascript/js_typed_array.h" #include "ecmascript/tagged_array-inl.h" #include "ecmascript/tagged_queue.h" +#include "ecmascript/tagged_dictionary.h" namespace panda::ecmascript { inline void ECMAObject::SetCallable(bool flag) @@ -360,6 +361,20 @@ inline bool JSObject::ShouldTransToDict(uint32_t capacity, uint32_t index) return false; } +inline bool JSObject::ShouldTransToFastElements(JSHandle dictionary, + uint32_t capacity, uint32_t index) +{ + if (index >= static_cast(INT32_MAX)) { + return false; + } + uint32_t dictionarySize = static_cast(dictionary->GetLength()); + // Turn fast if only saves 50% space. + if (dictionarySize * SHOULD_TRANS_TO_FAST_ELEMENTS_FACTOR >= capacity) { + return true; + } + return false; +} + inline uint32_t JSObject::ComputeElementCapacity(uint32_t oldCapacity, bool isNew) { uint32_t newCapacity = isNew ? oldCapacity : (oldCapacity + (oldCapacity >> 1U)); @@ -372,6 +387,18 @@ inline uint32_t JSObject::ComputeElementCapacityHighGrowth(uint32_t oldCapacity) return newCapacity > MIN_ELEMENTS_LENGTH ? newCapacity : MIN_ELEMENTS_LENGTH; } +inline uint32_t JSObject::ComputeElementCapacityWithHint(uint32_t oldCapacity, uint32_t hint) +{ + uint32_t newCapacity = 0; + if ((oldCapacity >= hint) || (hint < MIN_ELEMENTS_HINT_LENGTH) || (hint >= MAX_ELEMENTS_HINT_LENGTH)) { + return newCapacity; + } + if ((hint / oldCapacity) <= ELEMENTS_HINT_FACTOR) { + newCapacity = hint; + } + return newCapacity; +} + inline uint32_t JSObject::ComputeNonInlinedFastPropsCapacity(JSThread *thread, uint32_t oldCapacity, uint32_t maxNonInlinedFastPropsCapacity) { diff --git a/ecmascript/js_object.cpp b/ecmascript/js_object.cpp index 43fdd2ac5c8ec51b5cf0f7e7ed7f3b1d6b189ab8..89d3372e28bf9e524c8704ff9fe4dfa9c6523390 100644 --- a/ecmascript/js_object.cpp +++ b/ecmascript/js_object.cpp @@ -86,11 +86,14 @@ Method *ECMAObject::GetCallTarget() const JSHandle JSObject::GrowElementsCapacity(const JSThread *thread, const JSHandle &obj, uint32_t capacity, bool highGrowth, bool isNew) { - uint32_t newCapacity; - if (highGrowth) { - newCapacity = ComputeElementCapacityHighGrowth(capacity); - } else { - newCapacity = ComputeElementCapacity(capacity, isNew); + uint32_t newCapacity = 0; + if (obj->IsJSArray()) { + uint32_t hint = JSHandle(obj)->GetHintLength(); + newCapacity = ComputeElementCapacityWithHint(capacity, hint); + } + if (newCapacity == 0) { + newCapacity = highGrowth ? ComputeElementCapacityHighGrowth(capacity) : + ComputeElementCapacity(capacity, isNew); } ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle oldElements(thread, obj->GetElements()); @@ -330,10 +333,10 @@ bool JSObject::IsArrayLengthWritable(JSThread *thread, const JSHandle return op.GetAttr().IsWritable(); } -bool JSObject::AddElementInternal(JSThread *thread, const JSHandle &receiver, uint32_t index, - const JSHandle &value, PropertyAttributes attr) +bool JSObject::AddElementInternal(JSThread *thread, const JSHandle &receiver, + uint32_t index, const JSHandle &value, + PropertyAttributes attr) { - bool isDictionary = receiver->GetJSHClass()->IsDictionaryElement(); ElementsKind kind = ElementsKind::NONE; if (receiver->IsJSArray()) { DISALLOW_GARBAGE_COLLECTION; @@ -351,6 +354,18 @@ bool JSObject::AddElementInternal(JSThread *thread, const JSHandle &re } thread->NotifyStableArrayElementsGuardians(receiver, StableArrayChangeKind::NOT_PROTO); + // check whether to convert to dictionary + if (receiver->GetJSHClass()->IsDictionaryElement() && receiver->IsJSArray()) { + JSArray *arr = JSArray::Cast(*receiver); + uint32_t capacity = arr->GetArrayLength(); + TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject()); + ASSERT(elements->IsDictionaryMode()); + if (ShouldTransToFastElements(JSHandle(thread, elements), capacity, index)) { + JSObject::TryOptimizeAsFastElements(thread, receiver); + } + } + + bool isDictionary = receiver->GetJSHClass()->IsDictionaryElement(); TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject()); if (isDictionary) { ASSERT(elements->IsDictionaryMode()); diff --git a/ecmascript/js_object.h b/ecmascript/js_object.h index 27ac6141933ce9a24bbd430489a89da115c68109..de365cd7a8809ba4e517f865116d874dbfecad15 100644 --- a/ecmascript/js_object.h +++ b/ecmascript/js_object.h @@ -41,6 +41,7 @@ class JSForInIterator; class LexicalEnv; class GlobalEnv; class TaggedQueue; +class NumberDictionary; using EnumCacheKind = EnumCache::EnumCacheKind; // Integrity level for objects @@ -371,6 +372,10 @@ public: static constexpr int MIN_GAP = 256; static constexpr int MAX_GAP = 1_KB; static constexpr uint32_t MAX_ELEMENT_INDEX = std::numeric_limits::max(); + static constexpr int MIN_ELEMENTS_HINT_LENGTH = 1_KB; + static constexpr int MAX_ELEMENTS_HINT_LENGTH = 2_MB; + static constexpr int ELEMENTS_HINT_FACTOR = 8; + static constexpr int SHOULD_TRANS_TO_FAST_ELEMENTS_FACTOR = 2; CAST_CHECK(JSObject, IsECMAObject); @@ -656,6 +661,7 @@ public: static bool IsArrayLengthWritable(JSThread *thread, const JSHandle &receiver); bool UpdatePropertyInDictionary(const JSThread *thread, JSTaggedValue key, JSTaggedValue value); static bool ShouldTransToDict(uint32_t capacity, uint32_t index); + static bool ShouldTransToFastElements(JSHandle dictionary, uint32_t capacity, uint32_t index); static bool ShouldOptimizeAsFastElements(const JSThread *thread, JSHandle obj); static JSHandle GrowElementsCapacity(const JSThread *thread, const JSHandle &obj, uint32_t capacity, bool highGrowth = false, bool isNew = false); @@ -701,6 +707,7 @@ private: static uint32_t ComputeElementCapacity(uint32_t oldCapacity, bool isNew = false); static uint32_t ComputeElementCapacityHighGrowth(uint32_t oldCapacity); + static uint32_t ComputeElementCapacityWithHint(uint32_t oldCapacity, uint32_t hint); static uint32_t ComputeNonInlinedFastPropsCapacity(JSThread *thread, uint32_t oldCapacity, uint32_t maxNonInlinedFastPropsCapacity); diff --git a/ecmascript/js_stable_array.cpp b/ecmascript/js_stable_array.cpp index 4ea959655d9beb8d21b7870042fa7b64c7e78103..0b9536dd64efec404977850c3c829b84ff7ce62c 100644 --- a/ecmascript/js_stable_array.cpp +++ b/ecmascript/js_stable_array.cpp @@ -761,7 +761,12 @@ JSTaggedValue JSStableArray::Map(JSHandle newArrayHandle, JSHandleGetElements()); - kValue.Update(array->Get(k)); + JSTaggedValue value = array->Get(k); + if (value.IsHole() && JSTaggedValue::HasProperty(thread, thisObjVal, k)) { + value = JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + kValue.Update(value); if (!kValue.GetTaggedValue().IsHole()) { key.Update(JSTaggedValue(k)); EcmaRuntimeCallInfo *info = @@ -1074,8 +1079,8 @@ JSTaggedValue JSStableArray::Slice(JSThread *thread, JSHandle thisObjH JSHandle dstElements = factory->NewAndCopyTaggedArray(srcElements, count, oldLen, k); for (int i = 0; i < count; i++) { JSTaggedValue value = dstElements->Get(thread, i); - if (value.IsHole() && JSTaggedValue::HasProperty(thread, thisObjVal, i)) { - value = JSArray::FastGetPropertyByValue(thread, thisObjVal, i).GetTaggedValue(); + if (value.IsHole() && JSTaggedValue::HasProperty(thread, thisObjVal, i + k)) { + value = JSArray::FastGetPropertyByValue(thread, thisObjVal, i + k).GetTaggedValue(); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); dstElements->Set(thread, i, value); } diff --git a/ecmascript/js_thread.h b/ecmascript/js_thread.h index 958f471443a2f68405a7b9872ecffaa0797afd91..13b98b38e985dca4eff952ddae34bc869e182931 100644 --- a/ecmascript/js_thread.h +++ b/ecmascript/js_thread.h @@ -204,6 +204,11 @@ public: return glueData_.globalConst_; } + const BuiltinEntries GetBuiltinEntries() const + { + return glueData_.builtinEntries_; + } + const CMap &GetArrayHClassIndexMap() const { return arrayHClassIndexMap_; diff --git a/ecmascript/jspandafile/program_object.h b/ecmascript/jspandafile/program_object.h index 2bb3ca6f05ae608d3f49834cd721438be2670a38..3dbb47997dcbf250b64e159c85c66d035f776165 100644 --- a/ecmascript/jspandafile/program_object.h +++ b/ecmascript/jspandafile/program_object.h @@ -197,6 +197,11 @@ public: Barriers::SetPrimitive(GetData(), GetAotArrayInfoOffset(), info.GetRawData()); } + inline JSTaggedValue GetAotArrayInfo() + { + return JSTaggedValue(Barriers::GetValue(GetData(), GetAotArrayInfoOffset())); + } + inline void SetAotHClassInfo(JSTaggedValue info) { Barriers::SetPrimitive(GetData(), GetAotHClassInfoOffset(), info.GetRawData()); @@ -339,8 +344,13 @@ public: break; } case ConstPoolType::ARRAY_LITERAL: { - JSHandle literal = LiteralDataExtractor::GetDatasIgnoreType(thread, jsPandaFile, id, - constpoolHandle, entry, needSetAotFlag, entryIndexes); + // literal fetching from AOT ArrayInfos + JSMutableHandle literal(thread, JSTaggedValue::Undefined()); + if (!constpoolHandle->TryGetAOTArrayLiteral(thread, needSetAotFlag, entryIndexes, literal)) { + literal.Update(LiteralDataExtractor::GetDatasIgnoreType(thread, jsPandaFile, id, + constpoolHandle, entry, + needSetAotFlag, entryIndexes)); + } uint32_t length = literal->GetLength(); JSHandle arr(JSArray::ArrayCreate(thread, JSTaggedNumber(length), ArrayMode::LITERAL)); arr->SetElements(thread, literal); @@ -360,6 +370,21 @@ public: return val; } + bool TryGetAOTArrayLiteral(JSThread *thread, bool loadAOT, JSHandle entryIndexes, + JSMutableHandle literal) + { + if (loadAOT) { + int elementIndex = entryIndexes->GetElementIndex(); + if (elementIndex != kungfu::BaseSnapshotInfo::AOT_ELEMENT_INDEX_DEFAULT_VALUE) { + JSTaggedValue arrayInfos = GetAotArrayInfo(); + JSHandle aotArrayInfos(thread, arrayInfos); + literal.Update(aotArrayInfos->Get(elementIndex)); + return true; + } + } + return false; + } + static panda_file::File::EntityId GetIdFromCache(JSTaggedValue constpool, uint32_t index) { const ConstantPool *taggedPool = ConstantPool::Cast(constpool.GetTaggedObject()); diff --git a/ecmascript/napi/dfx_jsnapi.cpp b/ecmascript/napi/dfx_jsnapi.cpp index cc9ab9afbadec988b0714cb11e3b734824c8e34b..a78732fd9b05a8cb2adcd2df3e7b4e002e08340a 100644 --- a/ecmascript/napi/dfx_jsnapi.cpp +++ b/ecmascript/napi/dfx_jsnapi.cpp @@ -612,14 +612,16 @@ void DFXJSNApi::StopSampling([[maybe_unused]] const EcmaVM *vm) #endif } -bool DFXJSNApi::StartProfiler(EcmaVM *vm, const ProfilerOption &option, int32_t instanceId, - const DebuggerPostTask &debuggerPostTask) +// old process. +bool DFXJSNApi::StartProfiler(EcmaVM *vm, const ProfilerOption &option, uint32_t tid, + int32_t instanceId, const DebuggerPostTask &debuggerPostTask) { JSNApi::DebugOption debugOption; debugOption.libraryPath = option.libraryPath; if (option.profilerType == ProfilerType::CPU_PROFILER) { debugOption.isDebugMode = false; - if (JSNApi::StartDebugger(vm, debugOption, instanceId, debuggerPostTask)) { + if (JSNApi::NotifyDebugMode( + tid, vm, option.libraryPath, debugOption, instanceId, debuggerPostTask, false, false)) { StartCpuProfilerForInfo(vm, option.interval); return true; } else { @@ -628,7 +630,8 @@ bool DFXJSNApi::StartProfiler(EcmaVM *vm, const ProfilerOption &option, int32_t } } else { debugOption.isDebugMode = true; - return JSNApi::StartDebugger(vm, debugOption, instanceId, debuggerPostTask); + return JSNApi::JSNApi::NotifyDebugMode( + tid, vm, option.libraryPath, debugOption, instanceId, debuggerPostTask, true, true); } } diff --git a/ecmascript/napi/include/dfx_jsnapi.h b/ecmascript/napi/include/dfx_jsnapi.h index 01c22a0836c5df24feb4ecb0d02d91daabe65f96..74249ce3e6904afeafb070a8b2e2ad750a5ba0b2 100644 --- a/ecmascript/napi/include/dfx_jsnapi.h +++ b/ecmascript/napi/include/dfx_jsnapi.h @@ -100,8 +100,9 @@ public: int interval = 500; // 500:Default Sampling interval 500 microseconds ProfilerType profilerType = ProfilerType::CPU_PROFILER; }; - static bool StartProfiler(EcmaVM *vm, const ProfilerOption &option, int32_t instanceId, - const DebuggerPostTask &debuggerPostTask); + // To be compatible with old process. + static bool StartProfiler(EcmaVM *vm, const ProfilerOption &option, uint32_t tid, + int32_t instanceId, const DebuggerPostTask &debuggerPostTask); static void SetCpuSamplingInterval(const EcmaVM *vm, int interval); static bool StartSampling(const EcmaVM *vm, uint64_t samplingInterval); static const SamplingInfo *GetAllocationProfile(const EcmaVM *vm); diff --git a/ecmascript/napi/include/jsnapi.h b/ecmascript/napi/include/jsnapi.h index f449d80870172fc025139fd5333eba9fe50be427..bb4aeb4fd8017ca82db12a9be81508f6be315495 100644 --- a/ecmascript/napi/include/jsnapi.h +++ b/ecmascript/napi/include/jsnapi.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -776,13 +777,17 @@ public: }; using FunctionCallback = Local(*)(JsiRuntimeCallInfo*); +using InternalFunctionCallback = JSValueRef(*)(JsiRuntimeCallInfo*); class ECMA_PUBLIC_API FunctionRef : public ObjectRef { public: static Local New(EcmaVM *vm, FunctionCallback nativeFunc, Deleter deleter = nullptr, void *data = nullptr, bool callNapi = false, size_t nativeBindingsize = 0); + static Local New(EcmaVM *vm, InternalFunctionCallback nativeFunc, Deleter deleter, + void *data = nullptr, bool callNapi = false, size_t nativeBindingsize = 0); static Local NewClassFunction(EcmaVM *vm, FunctionCallback nativeFunc, Deleter deleter, void *data, bool callNapi = false, size_t nativeBindingsize = 0); - + static Local NewClassFunction(EcmaVM *vm, InternalFunctionCallback nativeFunc, Deleter deleter, + void *data, bool callNapi = false, size_t nativeBindingsize = 0); Local Call(const EcmaVM *vm, Local thisObj, const Local argv[], int32_t length); Local Constructor(const EcmaVM *vm, const Local argv[], int32_t length); @@ -1350,7 +1355,7 @@ class ECMA_PUBLIC_API JSNApi { public: struct DebugOption { const char *libraryPath; - bool isDebugMode; + bool isDebugMode = false; int port = -1; }; using DebuggerPostTask = std::function&&)>; @@ -1442,12 +1447,24 @@ public: static bool HasPendingException(const EcmaVM *vm); static bool HasPendingJob(const EcmaVM *vm); static void EnableUserUncaughtErrorHandler(EcmaVM *vm); + // prevewer debugger. static bool StartDebugger(EcmaVM *vm, const DebugOption &option, int32_t instanceId = 0, const DebuggerPostTask &debuggerPostTask = {}); + // To be compatible with the old process. + static bool StartDebuggerForOldProcess(EcmaVM *vm, const DebugOption &option, int32_t instanceId = 0, + const DebuggerPostTask &debuggerPostTask = {}); + // socketpair process in ohos platform. + static bool StartDebuggerForSocketPair(uint32_t tid, const DebugOption &option, int socketfd = -1, + const DebuggerPostTask &debuggerPostTask = {}); static bool StopDebugger(EcmaVM *vm); + static bool StopDebugger(uint32_t tid); + static bool NotifyDebugMode(uint32_t tid, EcmaVM *vm, const char *libraryPath, const DebugOption &option, + int32_t instanceId = 0, const DebuggerPostTask &debuggerPostTask = {}, + bool debugApp = false, bool debugMode = false); static bool IsMixedDebugEnabled(const EcmaVM *vm); static void NotifyNativeCalling(const EcmaVM *vm, const void *nativeAddress); static void NotifyNativeReturnJS(const EcmaVM *vm); + static void NotifyLoadModule(const EcmaVM *vm); static void SetDeviceDisconnectCallback(EcmaVM *vm, DeviceDisconnectCallback cb); // Serialize & Deserialize. static void* SerializeValue(const EcmaVM *vm, Local data, Local transfer); @@ -1469,8 +1486,10 @@ public: static EcmaVM* CreateEcmaVM(const ecmascript::JSRuntimeOptions &options); static void PreFork(EcmaVM *vm); static void PostFork(EcmaVM *vm, const RuntimeOption &option); - static void addWorker(EcmaVM *hostVm, EcmaVM *workerVm); + static void AddWorker(EcmaVM *hostVm, EcmaVM *workerVm); static bool DeleteWorker(EcmaVM *hostVm, EcmaVM *workerVm); + static void GetStackBeforeCallNapiSuccess(EcmaVM *vm, bool &getStackBeforeCallNapiSuccess); + static void GetStackAfterCallNapi(EcmaVM *vm); static PatchErrorCode LoadPatch(EcmaVM *vm, const std::string &patchFileName, const std::string &baseFileName); static PatchErrorCode LoadPatch(EcmaVM *vm, @@ -1509,9 +1528,12 @@ public: private: static int vmCount_; static bool initialize_; + static std::unordered_map> debugInfo_; static bool CreateRuntime(const RuntimeOption &option); static bool DestroyRuntime(); + static EcmaVM *GetEcmaVMByTid(uint32_t tid); + static const DebuggerPostTask &GetDebuggerTaskByTid(uint32_t tid); static uintptr_t GetHandleAddr(const EcmaVM *vm, uintptr_t localAddress); static uintptr_t GetGlobalHandleAddr(const EcmaVM *vm, uintptr_t localAddress); static uintptr_t SetWeak(const EcmaVM *vm, uintptr_t localAddress); diff --git a/ecmascript/napi/jsnapi.cpp b/ecmascript/napi/jsnapi.cpp index 212e7637e907dcf93898edcf92cfdc7671866f18..6c589a6c5b267b91982b568f8af5216346c251be 100644 --- a/ecmascript/napi/jsnapi.cpp +++ b/ecmascript/napi/jsnapi.cpp @@ -13,6 +13,7 @@ * limitations under the License. */ +#include "dfx_jsnapi.h" #include "jsnapi_helper.h" #include @@ -20,6 +21,7 @@ #include #include #include +#include #include "ecmascript/base/builtins_base.h" #include "ecmascript/base/json_parser.h" @@ -99,9 +101,10 @@ namespace OHOS::ArkCompiler::Toolchain { using DebuggerPostTask = std::function &&)>; extern "C" { - bool StartDebug(const std::string& componentName, void* vm, bool isDebugMode, int32_t instanceId, + bool StartDebug(const std::string& componentName, void* vm, int32_t instanceId, const DebuggerPostTask& debuggerPostTask, int port); void StopDebug(const std::string& componentName); + void WaitForDebugger(void* vm); } } // namespace OHOS::ArkCompiler::Toolchain const std::string DEBUGGER_NAME = "PandaDebugger"; @@ -200,6 +203,7 @@ constexpr std::string_view ENTRY_POINTER = "_GLOBAL::func_main_0"; } int JSNApi::vmCount_ = 0; bool JSNApi::initialize_ = false; +std::unordered_map> JSNApi::debugInfo_ {}; static Mutex *mutex = new panda::Mutex(); #define XPM_PROC_PREFIX "/proc/" #define XPM_PROC_SUFFIX "/xpm_region" @@ -423,6 +427,7 @@ void JSNApi::PrintExceptionInfo(const EcmaVM *vm) ThrowException(vm, exception); } +// for prevewer debugger. bool JSNApi::StartDebugger([[maybe_unused]] EcmaVM *vm, [[maybe_unused]] const DebugOption &option, [[maybe_unused]] int32_t instanceId, [[maybe_unused]] const DebuggerPostTask &debuggerPostTask) @@ -487,6 +492,169 @@ bool JSNApi::StartDebugger([[maybe_unused]] EcmaVM *vm, [[maybe_unused]] const D #endif // ECMASCRIPT_SUPPORT_DEBUGGER } +// for old process. +bool JSNApi::StartDebuggerForOldProcess([[maybe_unused]] EcmaVM *vm, [[maybe_unused]] const DebugOption &option, + [[maybe_unused]] int32_t instanceId, + [[maybe_unused]] const DebuggerPostTask &debuggerPostTask) +{ +#if defined(ECMASCRIPT_SUPPORT_DEBUGGER) +#if !defined(PANDA_TARGET_IOS) + if (vm == nullptr) { + return false; + } + CHECK_HAS_PENDING_EXCEPTION(vm, false); + const auto &handle = vm->GetJsDebuggerManager()->GetDebugLibraryHandle(); + if (!handle.IsValid()) { + LOG_ECMA(ERROR) << "[StartDebuggerForSocketPair] Get library handle fail: " << option.libraryPath; + return false; + } + + using StartDebugger = bool (*)( + const std::string &, EcmaVM *, int32_t, const DebuggerPostTask &, int); + + auto sym = panda::os::library_loader::ResolveSymbol(handle, "StartDebug"); + if (!sym) { + LOG_ECMA(ERROR) << "[StartDebugger] Resolve symbol fail: " << sym.Error().ToString(); + return false; + } + + bool ret = reinterpret_cast(sym.Value())( + "PandaDebugger", vm, instanceId, debuggerPostTask, option.port); + if (!ret) { + // Reset the config + vm->GetJsDebuggerManager()->SetDebugMode(false); + panda::os::library_loader::LibraryHandle libraryHandle(nullptr); + vm->GetJsDebuggerManager()->SetDebugLibraryHandle(std::move(libraryHandle)); + } + return ret; +#else + if (vm == nullptr) { + return false; + } + CHECK_HAS_PENDING_EXCEPTION(vm, false); + vm->GetJsDebuggerManager()->SetDebugMode(option.isDebugMode); + bool ret = OHOS::ArkCompiler::Toolchain::StartDebug( + DEBUGGER_NAME, vm, option.isDebugMode, instanceId, debuggerPostTask, option.port); + if (!ret) { + // Reset the config + vm->GetJsDebuggerManager()->SetDebugMode(false); + } + return ret; +#endif // PANDA_TARGET_IOS +#else + LOG_ECMA(ERROR) << "Not support arkcompiler debugger"; + return false; +#endif // ECMASCRIPT_SUPPORT_DEBUGGER +} + +// for socketpair process in ohos platform. +bool JSNApi::StartDebuggerForSocketPair([[maybe_unused]] uint32_t tid, + [[maybe_unused]] const DebugOption &option, + [[maybe_unused]] int socketfd, + [[maybe_unused]] const DebuggerPostTask &debuggerPostTask) +{ +#if defined(ECMASCRIPT_SUPPORT_DEBUGGER) + EcmaVM *vm = GetEcmaVMByTid(tid); + const DebuggerPostTask &realDebuggerPostTask = GetDebuggerTaskByTid(tid); + if (vm == nullptr) { + return false; + } + CHECK_HAS_PENDING_EXCEPTION(vm, false); + const auto &handle = vm->GetJsDebuggerManager()->GetDebugLibraryHandle(); + if (!handle.IsValid()) { + LOG_ECMA(ERROR) << "[StartDebuggerForSocketPair] Get library handle fail: " << option.libraryPath; + return false; + } + + using StartDebuggerForSocketPair = bool (*)(EcmaVM *, uint32_t, const DebuggerPostTask &, int); + + auto sym = panda::os::library_loader::ResolveSymbol(handle, "StartDebugForSocketpair"); + if (!sym) { + LOG_ECMA(ERROR) << "[StartDebuggerForSocketPair] Resolve symbol fail: " << sym.Error().ToString(); + return false; + } + + bool ret = reinterpret_cast(sym.Value())(vm, tid, realDebuggerPostTask, socketfd); + if (!ret) { + // Reset the config + vm->GetJsDebuggerManager()->SetDebugMode(false); + panda::os::library_loader::LibraryHandle libraryHandle(nullptr); + vm->GetJsDebuggerManager()->SetDebugLibraryHandle(std::move(libraryHandle)); + } + return ret; +#else + LOG_ECMA(ERROR) << "Not support arkcompiler debugger"; + return false; +#endif // ECMASCRIPT_SUPPORT_DEBUGGER +} + +EcmaVM *JSNApi::GetEcmaVMByTid(uint32_t tid) +{ + if (debugInfo_.find(tid) == debugInfo_.end()) { + return nullptr; + } + return debugInfo_[tid].first; +} + +const DebuggerPostTask &JSNApi::GetDebuggerTaskByTid(uint32_t tid) +{ + if (debugInfo_.find(tid) == debugInfo_.end()) { + return {}; + } + return debugInfo_[tid].second; +} + +bool JSNApi::NotifyDebugMode([[maybe_unused]] uint32_t tid, + [[maybe_unused]] EcmaVM *vm, + [[maybe_unused]] const char *libraryPath, + [[maybe_unused]] const DebugOption &option, + [[maybe_unused]] int32_t instanceId, + [[maybe_unused]] const DebuggerPostTask &debuggerPostTask, + [[maybe_unused]] bool debugApp, [[maybe_unused]] bool debugMode) +{ +#if defined(ECMASCRIPT_SUPPORT_DEBUGGER) + // debug app & -D; heap profile + if (vm == nullptr) { + return false; + } + if (debugInfo_.find(tid) == debugInfo_.end()) { + debugInfo_.emplace(tid, std::make_pair(vm, debuggerPostTask)); + } + + CHECK_HAS_PENDING_EXCEPTION(vm, false); + const auto &handler = vm->GetJsDebuggerManager()->GetDebugLibraryHandle(); + if (handler.IsValid()) { + return false; + } + + auto handle = panda::os::library_loader::Load(std::string(libraryPath)); + if (!handle) { + LOG_ECMA(ERROR) << "[NotifyDebugMode] Load library fail: " << libraryPath << " " << errno; + return false; + } + vm->GetJsDebuggerManager()->SetDebugLibraryHandle(std::move(handle.Value())); + vm->GetJsDebuggerManager()->SetDebugMode(option.isDebugMode && debugApp); + vm->GetJsDebuggerManager()->SetIsDebugApp(debugApp); + bool ret = StartDebuggerForOldProcess(vm, option, instanceId, debuggerPostTask); + + if (debugApp && debugMode) { + using WaitForDebugger = bool (*)(EcmaVM *); + + auto sym = panda::os::library_loader::ResolveSymbol( + vm->GetJsDebuggerManager()->GetDebugLibraryHandle(), "WaitForDebugger"); + if (!sym) { + LOG_ECMA(ERROR) << "[NotifyDebugMode] Resolve symbol fail: " << sym.Error().ToString(); + return false; + } + reinterpret_cast(sym.Value())(vm); + } + return ret; +#else + LOG_ECMA(ERROR) << "Not support arkcompiler debugger"; + return false; +#endif // ECMASCRIPT_SUPPORT_DEBUGGER +} + bool JSNApi::StopDebugger([[maybe_unused]] EcmaVM *vm) { #if defined(ECMASCRIPT_SUPPORT_DEBUGGER) @@ -526,6 +694,46 @@ bool JSNApi::StopDebugger([[maybe_unused]] EcmaVM *vm) #endif // ECMASCRIPT_SUPPORT_DEBUGGER } +bool JSNApi::StopDebugger(uint32_t tid) +{ +#if defined(ECMASCRIPT_SUPPORT_DEBUGGER) +#if !defined(PANDA_TARGET_IOS) + EcmaVM *vm = GetEcmaVMByTid(tid); + if (vm == nullptr) { + return false; + } + CHECK_HAS_PENDING_EXCEPTION(vm, false); + + const auto &handle = vm->GetJsDebuggerManager()->GetDebugLibraryHandle(); + + using StopOldDebug = void (*)(void *, const std::string &); + + auto sym = panda::os::library_loader::ResolveSymbol(handle, "StopOldDebug"); + if (!sym) { + LOG_ECMA(ERROR) << sym.Error().ToString(); + return false; + } + + reinterpret_cast(sym.Value())(vm, "PandaDebugger"); + + vm->GetJsDebuggerManager()->SetDebugMode(false); + return true; +#else + if (vm == nullptr) { + return false; + } + CHECK_HAS_PENDING_EXCEPTION(vm, false); + + OHOS::ArkCompiler::Toolchain::StopDebug(DEBUGGER_NAME); + vm->GetJsDebuggerManager()->SetDebugMode(false); + return true; +#endif // PANDA_TARGET_IOS +#else + LOG_ECMA(ERROR) << "Not support arkcompiler debugger"; + return false; +#endif // ECMASCRIPT_SUPPORT_DEBUGGER +} + bool JSNApi::IsMixedDebugEnabled([[maybe_unused]] const EcmaVM *vm) { #if defined(ECMASCRIPT_SUPPORT_DEBUGGER) @@ -556,6 +764,17 @@ void JSNApi::NotifyNativeReturnJS([[maybe_unused]] const EcmaVM *vm) #endif } +void JSNApi::NotifyLoadModule([[maybe_unused]] const EcmaVM *vm) +{ +#if defined(ECMASCRIPT_SUPPORT_DEBUGGER) + CHECK_HAS_PENDING_EXCEPTION_WITHOUT_RETURN(vm); + // if load module, it needs to check whether clear singlestepper_ + vm->GetJsDebuggerManager()->ClearSingleStepper(); +#else + LOG_ECMA(ERROR) << "Not support arkcompiler debugger"; +#endif +} + void JSNApi::SetDeviceDisconnectCallback(EcmaVM *vm, DeviceDisconnectCallback cb) { vm->SetDeviceDisconnectCallback(cb); @@ -689,7 +908,7 @@ void JSNApi::PostFork(EcmaVM *vm, const RuntimeOption &option) vm->PostFork(); } -void JSNApi::addWorker(EcmaVM *hostVm, EcmaVM *workerVm) +void JSNApi::AddWorker(EcmaVM *hostVm, EcmaVM *workerVm) { if (hostVm != nullptr && workerVm != nullptr) { hostVm->WorkersetInfo(workerVm); @@ -1715,6 +1934,36 @@ Local FunctionRef::New(EcmaVM *vm, FunctionCallback nativeFunc, return JSNApiHelper::ToLocal(JSHandle(current)); } +Local FunctionRef::New(EcmaVM *vm, InternalFunctionCallback nativeFunc, + Deleter deleter, void *data, bool callNapi, size_t nativeBindingsize) +{ + CHECK_HAS_PENDING_EXCEPTION_RETURN_UNDEFINED(vm); + JSThread *thread = vm->GetJSThread(); + ObjectFactory *factory = vm->GetFactory(); + JSHandle env = vm->GetGlobalEnv(); + JSHandle current(factory->NewJSFunction(env, reinterpret_cast(nativeFunc))); + current->SetFunctionExtraInfo(thread, nullptr, deleter, data, nativeBindingsize); + current->SetCallNapi(callNapi); + return JSNApiHelper::ToLocal(JSHandle(current)); +} + +static void InitClassFunction(EcmaVM *vm, JSHandle &func, bool callNapi) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle env = vm->GetGlobalEnv(); + auto globalConst = thread->GlobalConstants(); + JSHandle accessor = globalConst->GetHandledFunctionPrototypeAccessor(); + func->SetPropertyInlinedProps(thread, JSFunction::CLASS_PROTOTYPE_INLINE_PROPERTY_INDEX, + accessor.GetTaggedValue()); + JSHandle clsPrototype = JSFunction::NewJSFunctionPrototype(thread, func); + clsPrototype->GetClass()->SetClassPrototype(true); + func->SetClassConstructor(true); + JSHandle parent = env->GetFunctionPrototype(); + JSObject::SetPrototype(thread, JSHandle::Cast(func), parent); + func->SetHomeObject(thread, clsPrototype); + func->SetCallNapi(callNapi); +} + Local FunctionRef::NewClassFunction(EcmaVM *vm, FunctionCallback nativeFunc, Deleter deleter, void *data, bool callNapi, size_t nativeBindingsize) { @@ -1727,22 +1976,26 @@ Local FunctionRef::NewClassFunction(EcmaVM *vm, FunctionCallback na JSHandle current = factory->NewJSFunctionByHClass(reinterpret_cast(Callback::RegisterCallback), hclass, ecmascript::FunctionKind::CLASS_CONSTRUCTOR); - - auto globalConst = thread->GlobalConstants(); - JSHandle accessor = globalConst->GetHandledFunctionPrototypeAccessor(); - current->SetPropertyInlinedProps(thread, JSFunction::CLASS_PROTOTYPE_INLINE_PROPERTY_INDEX, - accessor.GetTaggedValue()); - + InitClassFunction(vm, current, callNapi); current->SetFunctionExtraInfo(thread, reinterpret_cast(nativeFunc), deleter, data, nativeBindingsize); + Local result = JSNApiHelper::ToLocal(JSHandle(current)); + return scope.Escape(result); +} - JSHandle clsPrototype = JSFunction::NewJSFunctionPrototype(thread, current); - clsPrototype.GetTaggedValue().GetTaggedObject()->GetClass()->SetClassPrototype(true); - JSHandle::Cast(current)->GetTaggedObject()->GetClass()->SetClassConstructor(true); - current->SetClassConstructor(true); - JSHandle parent = env->GetFunctionPrototype(); - JSObject::SetPrototype(thread, JSHandle::Cast(current), parent); - current->SetHomeObject(thread, clsPrototype); - current->SetCallNapi(callNapi); +Local FunctionRef::NewClassFunction(EcmaVM *vm, InternalFunctionCallback nativeFunc, + Deleter deleter, void *data, bool callNapi, size_t nativeBindingsize) +{ + CHECK_HAS_PENDING_EXCEPTION_RETURN_UNDEFINED(vm); + EscapeLocalScope scope(vm); + JSThread *thread = vm->GetJSThread(); + ObjectFactory *factory = vm->GetFactory(); + JSHandle env = vm->GetGlobalEnv(); + JSHandle hclass = JSHandle::Cast(env->GetFunctionClassWithoutName()); + JSHandle current = + factory->NewJSFunctionByHClass(reinterpret_cast(nativeFunc), + hclass, ecmascript::FunctionKind::CLASS_CONSTRUCTOR); + InitClassFunction(vm, current, callNapi); + current->SetFunctionExtraInfo(thread, nullptr, deleter, data, nativeBindingsize); Local result = JSNApiHelper::ToLocal(JSHandle(current)); return scope.Escape(result); } @@ -3802,6 +4055,27 @@ void JSNApi::SetSourceMapTranslateCallback(EcmaVM *vm, SourceMapTranslateCallbac vm->SetSourceMapTranslateCallback(callback); } +void JSNApi::GetStackBeforeCallNapiSuccess([[maybe_unused]] EcmaVM *vm, + [[maybe_unused]] bool &getStackBeforeCallNapiSuccess) +{ +#if defined(ECMASCRIPT_SUPPORT_CPUPROFILER) + JSThread *thread = vm->GetJSThread(); + if (thread->GetIsProfiling()) { + getStackBeforeCallNapiSuccess = vm->GetProfiler()->GetStackBeforeCallNapi(thread); + } +#endif +} + +void JSNApi::GetStackAfterCallNapi([[maybe_unused]] EcmaVM *vm) +{ +#if defined(ECMASCRIPT_SUPPORT_CPUPROFILER) + JSThread *thread = vm->GetJSThread(); + if (thread->GetIsProfiling()) { + vm->GetProfiler()->GetStackAfterCallNapi(thread); + } +#endif +} + TryCatch::~TryCatch() {} bool TryCatch::HasCaught() const diff --git a/ecmascript/napi/test/jsnapi_tests.cpp b/ecmascript/napi/test/jsnapi_tests.cpp index 2eb6cff95200d55b6a168276e7e039ff6ebf1c3f..88e1264cf050d52d6122fe907fd20bb5638fa364 100644 --- a/ecmascript/napi/test/jsnapi_tests.cpp +++ b/ecmascript/napi/test/jsnapi_tests.cpp @@ -1040,7 +1040,7 @@ HWTEST_F_L0(JSNApiTests, InheritPrototype_004) HWTEST_F_L0(JSNApiTests, ClassFunction) { LocalScope scope(vm_); - Local cls = FunctionRef::NewClassFunction(vm_, nullptr, nullptr, nullptr); + Local cls = FunctionRef::NewClassFunction(vm_, FunctionCallback, nullptr, nullptr); JSHandle clsObj = JSNApiHelper::ToJSHandle(Local(cls)); ASSERT_TRUE(clsObj->IsClassConstructor()); @@ -1128,7 +1128,7 @@ HWTEST_F_L0(JSNApiTests, addWorker_DeleteWorker) { JSRuntimeOptions option; EcmaVM *workerVm = JSNApi::CreateEcmaVM(option); - JSNApi::addWorker(vm_, workerVm); + JSNApi::AddWorker(vm_, workerVm); bool hasDeleted = JSNApi::DeleteWorker(vm_, workerVm); EXPECT_TRUE(hasDeleted); diff --git a/ecmascript/object_factory.cpp b/ecmascript/object_factory.cpp index 52f2b32c89750b182176dd7a799076e5fbc7ee5c..d047ec995bdc4e522b9ec056cada1a9f8acc15bf 100644 --- a/ecmascript/object_factory.cpp +++ b/ecmascript/object_factory.cpp @@ -671,6 +671,32 @@ JSHandle ObjectFactory::NewJSArray() return JSHandle(NewJSObjectByConstructor(function)); } +JSHandle ObjectFactory::NewJSArray(size_t length, JSHandle &hclass) +{ + JSHandle obj = NewJSObject(hclass); + JSArray::Cast(*obj)->SetLength(length); + JSArray::Cast(*obj)->SetTrackInfo(thread_, JSTaggedValue::Undefined()); + auto accessor = thread_->GlobalConstants()->GetArrayLengthAccessor(); + JSArray::Cast(*obj)->SetPropertyInlinedProps(thread_, JSArray::LENGTH_INLINE_PROPERTY_INDEX, accessor); + return JSHandle(obj); +} + +JSHandle ObjectFactory::NewJsonFixedArray(size_t start, size_t length, + const std::vector> &vec) +{ + if (length == 0) { + return EmptyArray(); + } + + MemSpaceType spaceType = length < LENGTH_THRESHOLD ? MemSpaceType::SEMI_SPACE : MemSpaceType::OLD_SPACE; + JSHandle array = NewTaggedArrayWithoutInit(length, spaceType); + array->SetExtraLength(0); + for (size_t i = 0; i < length; i++) { + array->Set(thread_, i, vec[start + i]); + } + return array; +} + JSHandle ObjectFactory::NewJSForinIterator(const JSHandle &obj, const JSHandle keys, const JSHandle cachedHclass) @@ -881,6 +907,7 @@ JSHandle ObjectFactory::NewJSError(const ErrorType &errorType, const J info->SetCallArg(message.GetTaggedValue()); Method *method = JSHandle::Cast(ctor)->GetCallTarget(); JSTaggedValue obj = reinterpret_cast(const_cast(method->GetNativePointer()))(info); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread_); JSHandle handleNativeInstanceObj(thread_, obj); auto sp = const_cast(thread_->GetCurrentSPFrame()); ASSERT(FrameHandler::GetFrameType(sp) == FrameType::INTERPRETER_ENTRY_FRAME); @@ -2672,10 +2699,7 @@ EcmaString *ObjectFactory::GetRawStringFromStringTable(StringData sd, MemSpaceTy return vm_->GetEcmaStringTable()->GetOrInternStringWithSpaceType(mutf8Data, utf16Len, true, type, isConstantString, idOffset); } - - CVector utf16Data(utf16Len); - auto len = utf::ConvertRegionMUtf8ToUtf16(mutf8Data, utf16Data.data(), utf::Mutf8Size(mutf8Data), utf16Len, 0); - return vm_->GetEcmaStringTable()->GetOrInternStringWithSpaceType(utf16Data.data(), len, false, type); + return vm_->GetEcmaStringTable()->GetOrInternStringWithSpaceType(mutf8Data, utf16Len, type); } JSHandle ObjectFactory::NewPropertyBox(const JSHandle &value) diff --git a/ecmascript/object_factory.h b/ecmascript/object_factory.h index 9525803d0b0f9529b6aa76853f6012e40c52f447..f2174dfc2d5a72edefa14c2a652cbad406d3015e 100644 --- a/ecmascript/object_factory.h +++ b/ecmascript/object_factory.h @@ -317,6 +317,9 @@ public: JSHandle NewPendingJob(const JSHandle &func, const JSHandle &argv); JSHandle NewJSArray(); + JSHandle NewJSArray(size_t length, JSHandle &hclass); + JSHandle NewJsonFixedArray(size_t start, size_t length, + const std::vector> &vec); JSHandle NewJSProxy(const JSHandle &target, const JSHandle &handler); JSHandle NewJSRealm(); diff --git a/ecmascript/ohos/ohos_pkg_args.h b/ecmascript/ohos/ohos_pkg_args.h index 46cab2c3ab8c65a3d6f8f4e86822a4ed55689057..e5614e7160518cfba34d63a14f2d83c26cadf32a 100644 --- a/ecmascript/ohos/ohos_pkg_args.h +++ b/ecmascript/ohos/ohos_pkg_args.h @@ -54,6 +54,7 @@ public: constexpr static const char *const KEY_PROCESS_UID = "processUid"; constexpr static const char *const KEY_BUNDLE_UID = "bundleUid"; constexpr static const char *const IS_ENCRYPTED_BUNDLE = "isEncryptedBundle"; + constexpr static const char *const APP_IDENTIFIER = "appIdentifier"; OhosPkgArgs() = default; @@ -259,6 +260,8 @@ public: } else if (strcmp(key, IS_ENCRYPTED_BUNDLE) == 0) { char *str = nullptr; IsEncryptedBundle_ = static_cast(strtol(value, &str, 0)); + } else if (strcmp(key, APP_IDENTIFIER) == 0) { + appSignature_ = value; } else { LOG_COMPILER(ERROR) << "Unknown keyword when parse pkg info. key: " << key << ", value: " << value; } @@ -285,7 +288,8 @@ public: << KEY_PGO_DIR << ": " << pgoDir_ << ", " << KEY_BUNDLE_UID << ": " << bundleUid_ << ", " << KEY_PROCESS_UID << ": " << processUid_ << ", " - << IS_ENCRYPTED_BUNDLE << ": " << IsEncryptedBundle_ ; + << IS_ENCRYPTED_BUNDLE << ": " << IsEncryptedBundle_ + << APP_IDENTIFIER << ": " << appSignature_; } const std::string &GetBundleName() const @@ -303,6 +307,11 @@ public: return pkgPath_; } + const std::string &GetAppSignature() const + { + return appSignature_; + } + std::string GetFullName() const { return pkgPath_ + GetPathSeparator() + moduleName_ + GetPathSeparator() + abcName_; @@ -445,6 +454,7 @@ private: std::string pkgPath_{""}; std::string abcName_{""}; std::string pgoDir_{""}; + std::string appSignature_{""}; uint32_t abcOffset_ {INVALID_VALUE}; uint32_t abcSize_ {INVALID_VALUE}; uint32_t bundleUid_ {INVALID_VALUE}; diff --git a/ecmascript/platform/code_sign.h b/ecmascript/platform/code_sign.h index 8a3be1b253b4289c32bdb28f98f7956b693f1f24..d819fe3186dd89f793be82d5c516fdfb4c6b0028 100644 --- a/ecmascript/platform/code_sign.h +++ b/ecmascript/platform/code_sign.h @@ -19,6 +19,6 @@ #include namespace panda::ecmascript { -void CodeSignForAOTFile(const std::string &filename); +void CodeSignatureForAOTFile(const std::string &filename, const std::string &appSignature); } // namespace panda::ecmascript #endif // ECMASCRIPT_PLATFORM_CODE_SIGN_H diff --git a/ecmascript/platform/unix/code_sign.cpp b/ecmascript/platform/unix/code_sign.cpp index c82cfbb5ba48957e2a3154b3718343bc83e153c2..ab06a143513c778f189c2b6d6bd63ca23ea06535 100644 --- a/ecmascript/platform/unix/code_sign.cpp +++ b/ecmascript/platform/unix/code_sign.cpp @@ -16,8 +16,9 @@ #include "ecmascript/platform/code_sign.h" namespace panda::ecmascript { -void CodeSignForAOTFile(const std::string &filename) +void CodeSignatureForAOTFile(const std::string &filename, const std::string &appSignature) { (void)filename; + (void)appSignature; } } // namespace panda::ecmascript \ No newline at end of file diff --git a/ecmascript/platform/unix/ohos/code_sign.cpp b/ecmascript/platform/unix/ohos/code_sign.cpp index 1a7fdaad0344930d136c162d4846a45a2a45b991..d34a2960886056afa042d0106e4ebcfe22e5ebb6 100644 --- a/ecmascript/platform/unix/ohos/code_sign.cpp +++ b/ecmascript/platform/unix/ohos/code_sign.cpp @@ -22,11 +22,11 @@ using namespace OHOS::Security::CodeSign; namespace panda::ecmascript { -void CodeSignForAOTFile(const std::string &filename) +void CodeSignatureForAOTFile(const std::string &filename, const std::string &appSignature) { LOG_ECMA(DEBUG) << "start to sign the aot file!"; ByteBuffer sig; - if (LocalCodeSignKit::SignLocalCode(filename, sig) != CommonErrCode::CS_SUCCESS) { + if (LocalCodeSignKit::SignLocalCode(appSignature, filename, sig) != CommonErrCode::CS_SUCCESS) { LOG_ECMA(ERROR) << "Failed to sign the aot file!"; return; } diff --git a/ecmascript/platform/windows/code_sign.cpp b/ecmascript/platform/windows/code_sign.cpp index c82cfbb5ba48957e2a3154b3718343bc83e153c2..ab06a143513c778f189c2b6d6bd63ca23ea06535 100644 --- a/ecmascript/platform/windows/code_sign.cpp +++ b/ecmascript/platform/windows/code_sign.cpp @@ -16,8 +16,9 @@ #include "ecmascript/platform/code_sign.h" namespace panda::ecmascript { -void CodeSignForAOTFile(const std::string &filename) +void CodeSignatureForAOTFile(const std::string &filename, const std::string &appSignature) { (void)filename; + (void)appSignature; } } // namespace panda::ecmascript \ No newline at end of file diff --git a/ecmascript/stubs/runtime_stubs.cpp b/ecmascript/stubs/runtime_stubs.cpp index 38449f0d2d4eca2e36d2e8886e7f2324642ec652..4f6c3ecd8aa3f77917404848eb879d7eeca0bc86 100644 --- a/ecmascript/stubs/runtime_stubs.cpp +++ b/ecmascript/stubs/runtime_stubs.cpp @@ -691,6 +691,14 @@ DEF_RUNTIME_STUBS(CallJSObjDeletePrototype) return JSTaggedValue::True().GetRawData(); } +DEF_RUNTIME_STUBS(ToPropertyKey) +{ + RUNTIME_STUBS_HEADER(ToPropertyKey); + JSHandle key = GetHArg(argv, argc, 0); // 0: means the zeroth parameter + JSTaggedValue res = JSTaggedValue::ToPropertyKey(thread, key).GetTaggedValue(); + return res.GetRawData(); +} + DEF_RUNTIME_STUBS(Exp) { RUNTIME_STUBS_HEADER(Exp); @@ -2251,6 +2259,14 @@ DEF_RUNTIME_STUBS(FastCopyElementToArray) return JSTaggedValue(JSTypedArray::FastCopyElementToArray(thread, typedArray, array)).GetRawData(); } +DEF_RUNTIME_STUBS(GetPropertyByName) +{ + RUNTIME_STUBS_HEADER(GetPropertyByName); + JSHandle target = GetHArg(argv, argc, 0); // 0: means the zeroth parameter + JSHandle key = GetHArg(argv, argc, 1); // 1: means the first parameter + return JSTaggedValue::GetProperty(thread, target, key).GetValue()->GetRawData(); +} + DEF_RUNTIME_STUBS(DebugAOTPrint) { RUNTIME_STUBS_HEADER(DebugAOTPrint); diff --git a/ecmascript/stubs/runtime_stubs.h b/ecmascript/stubs/runtime_stubs.h index 19fdd642d69aa34cd7c265a6a57d9af59d33ce3a..f82c010da8b391df31451309cc9adaa448813982 100644 --- a/ecmascript/stubs/runtime_stubs.h +++ b/ecmascript/stubs/runtime_stubs.h @@ -144,6 +144,7 @@ using FastCallAotEntryType = JSTaggedValue (*)(uintptr_t glue, uint32_t argc, co V(CallModuleNamespaceDeletePrototype) \ V(CallTypedArrayDeletePrototype) \ V(CallJSObjDeletePrototype) \ + V(ToPropertyKey) \ V(NewJSPrimitiveRef) \ V(ThrowTypeError) \ V(GetHash32) \ @@ -316,6 +317,7 @@ using FastCallAotEntryType = JSTaggedValue (*)(uintptr_t glue, uint32_t argc, co V(GetTypeArrayPropertyByIndex) \ V(SetTypeArrayPropertyByIndex) \ V(FastCopyElementToArray) \ + V(GetPropertyByName) \ V(JSObjectGetMethod) \ V(DebugAOTPrint) \ V(ProfileOptimizedCode) \ diff --git a/ecmascript/tagged_dictionary.cpp b/ecmascript/tagged_dictionary.cpp index 1aabbbf1fabcad846da6f08559dd135ffdcfca58..86b55a7c3f5759c75839613425f54d995fa45e9f 100644 --- a/ecmascript/tagged_dictionary.cpp +++ b/ecmascript/tagged_dictionary.cpp @@ -407,4 +407,31 @@ void NumberDictionary::ClearEntry(const JSThread *thread, int entry) PropertyAttributes metaData; SetEntry(thread, entry, hole, hole, metaData); } + +JSHandle PointerToIndexDictionary::Create(const JSThread *thread, int numberOfElements) +{ + return OrderHashTableT::Create(thread, numberOfElements); +} + +JSHandle PointerToIndexDictionary::PutIfAbsent( + const JSThread *thread, + const JSHandle &dictionary, + const JSHandle &key, + const JSHandle &value) +{ + /* no need to add key if exist */ + int entry = dictionary->FindEntry(key.GetTaggedValue()); + if (entry != -1) { + return dictionary; + } + // Check whether the table should be growed + JSHandle newDictionary = HashTableT::GrowHashTable(thread, dictionary); + + // Compute the key object + int32_t hash = PointerToIndexDictionary::Hash(key.GetTaggedValue()); + entry = newDictionary->FindInsertIndex(hash); + newDictionary->SetEntry(thread, entry, key.GetTaggedValue(), value.GetTaggedValue()); + newDictionary->IncreaseEntries(thread); + return newDictionary; +} } // namespace panda::ecmascript diff --git a/ecmascript/tagged_dictionary.h b/ecmascript/tagged_dictionary.h index 62ef433e65034f190826b29a74c9ea1c14e1cac9..40184b9340208e3539affa967001190dafb8bff9 100644 --- a/ecmascript/tagged_dictionary.h +++ b/ecmascript/tagged_dictionary.h @@ -140,5 +140,56 @@ public: static constexpr int ENTRY_DETAILS_INDEX = 2; static constexpr int ENTRY_SIZE = 3; }; + +class PointerToIndexDictionary : public OrderTaggedHashTable { +public: + using OrderHashTableT = OrderTaggedHashTable; + inline static int GetKeyIndex(int entry) + { + return OrderHashTableT::TABLE_HEADER_SIZE + entry * GetEntrySize() + ENTRY_KEY_INDEX; + } + + inline static int GetValueIndex(int entry) + { + return OrderHashTableT::TABLE_HEADER_SIZE + entry * GetEntrySize() + ENTRY_VALUE_INDEX; + } + + inline static int GetEntryIndex(int entry) + { + return OrderHashTableT::TABLE_HEADER_SIZE + entry * GetEntrySize(); + } + + inline static int GetEntrySize() + { + return ENTRY_SIZE; + } + + inline void SetEntry(const JSThread *thread, int entry, const JSTaggedValue &key, const JSTaggedValue &value) + { + SetKey(thread, entry, key); + SetValue(thread, entry, value); + } + + static int32_t Hash(const JSTaggedValue &key) + { + return static_cast(key.GetRawData()); + } + + static bool IsMatch(const JSTaggedValue &key, const JSTaggedValue &other) + { + return key == other; + } + static JSHandle Create(const JSThread *thread, + int numberOfElements = OrderHashTableT::DEFAULT_ELEMENTS_NUMBER); + static JSHandle PutIfAbsent( + const JSThread *thread, const JSHandle &dictionary, + const JSHandle &key, const JSHandle &value); + + // DECL_DUMP() + + static constexpr int ENTRY_KEY_INDEX = 0; + static constexpr int ENTRY_VALUE_INDEX = 1; + static constexpr int ENTRY_SIZE = 2; +}; } // namespace panda::ecmascript #endif // ECMASCRIPT_NEW_DICTIONARY_H diff --git a/ecmascript/taskpool/runner.cpp b/ecmascript/taskpool/runner.cpp index 26a05a65337757a1c53000bebe0ba88b3d6b586c..52c94bb38be3c5a46b2c7bbb66cedf8db865c952 100644 --- a/ecmascript/taskpool/runner.cpp +++ b/ecmascript/taskpool/runner.cpp @@ -95,7 +95,7 @@ void Runner::SetRunTask(uint32_t threadId, Task *task) void Runner::Run(uint32_t threadId) { os::thread::native_handle_type thread = os::thread::GetNativeHandle(); - os::thread::SetThreadName(thread, "GC_WorkerThread"); + os::thread::SetThreadName(thread, "OS_GC_WorkerThread"); RecordThreadId(); while (std::unique_ptr task = taskQueue_.PopTask()) { SetRunTask(threadId, task.get()); diff --git a/ecmascript/tests/BUILD.gn b/ecmascript/tests/BUILD.gn index 3000fd552a29758a4bdc18ae3e37ca973b525bca..31b3d3d6ca9d15baebca989ae7a7b20c00a3e910 100644 --- a/ecmascript/tests/BUILD.gn +++ b/ecmascript/tests/BUILD.gn @@ -38,6 +38,7 @@ host_unittest_action("EcmaVm_001_Test") { "frame_test.cpp", "gc_test.cpp", "global_dictionary_test.cpp", + "global_index_map_test.cpp", "glue_regs_test.cpp", "handle_leak_test.cpp", "huge_object_test.cpp", diff --git a/ecmascript/tests/global_index_map_test.cpp b/ecmascript/tests/global_index_map_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..73a4c1a7d60fef31c585ea8b4df7c7ef7c780f45 --- /dev/null +++ b/ecmascript/tests/global_index_map_test.cpp @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/builtin_entries.h" +#include "ecmascript/global_env.h" +#include "ecmascript/global_env_constants.h" +#include "ecmascript/global_index_map.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda::ecmascript; + +namespace panda::test { +class GlobalIndexMapTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + EcmaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +/** + * @tc.name: InitGlobalIndexMap + * @tc.desc: Check whether InitGlobalIndexMap can initialize dictionary. + * @tc.type: FUNC + * @tc.require: + */ +HWTEST_F_L0(GlobalIndexMapTest, GlobalIndexMap_initGlobalIndexMap) +{ + JSMutableHandle globalIndexMap( + GlobalIndexMap::GetGlobalIndexMap(thread->GetCurrentEcmaContext()).GetAddress()); + EXPECT_NE(globalIndexMap.GetTaggedValue().IsHeapObject(), true); + GlobalIndexMap::InitGlobalIndexMap(thread, globalIndexMap); + globalIndexMap.Update(GlobalIndexMap::GetGlobalIndexMap(thread->GetCurrentEcmaContext())); + EXPECT_EQ(globalIndexMap.GetTaggedValue().IsHeapObject(), true); +} + +/** + * @tc.name: InitGlobalConst + * @tc.desc: Check whether GlobalConst can be find in the GlobalMap. + * @tc.type: FUNC + * @tc.require: + */ +HWTEST_F_L0(GlobalIndexMapTest, GlobalIndexMap_initGlobalConst) +{ + JSMutableHandle globalIndexMap( + GlobalIndexMap::GetGlobalIndexMap(thread->GetCurrentEcmaContext()).GetAddress()); + EXPECT_NE(globalIndexMap.GetTaggedValue().IsHeapObject(), true); + GlobalIndexMap::InitGlobalIndexMap(thread, globalIndexMap); + GlobalIndexMap::InitGlobalConst(thread, globalIndexMap); + globalIndexMap.Update(GlobalIndexMap::GetGlobalIndexMap(thread->GetCurrentEcmaContext())); + EXPECT_EQ(globalIndexMap.GetTaggedValue().IsHeapObject(), true); + auto globalConst = const_cast(thread->GlobalConstants()); + uint32_t constantCount = globalConst->GetConstantCount(); + for (uint32_t index = 0; index < constantCount; index++) { + JSTaggedValue objectValue = globalConst->GetGlobalConstantObject(index); + if (objectValue.IsHeapObject() && !objectValue.IsString()) { + GlobalIndex rootIndex; + GlobalIndexMap::FindGlobalIndex(globalIndexMap, objectValue, &rootIndex); + EXPECT_EQ(static_cast(index), rootIndex.GetGlobalConstId()); + GlobalIndex globalConstGlobalIndex; + globalConstGlobalIndex.UpdateGlobalConstId(index); + EXPECT_EQ(globalConstGlobalIndex, rootIndex); + } + } +} + +/** + * @tc.name: InitGlobalEnv + * @tc.desc: Check whether GlobalEnv can be find in the GlobalMap. + * @tc.type: FUNC + * @tc.require: + */ +HWTEST_F_L0(GlobalIndexMapTest, GlobalIndexMap_initGlobalEnv) +{ + JSMutableHandle globalIndexMap( + GlobalIndexMap::GetGlobalIndexMap(thread->GetCurrentEcmaContext()).GetAddress()); + EXPECT_NE(globalIndexMap.GetTaggedValue().IsHeapObject(), true); + GlobalIndexMap::InitGlobalIndexMap(thread, globalIndexMap); + GlobalIndexMap::InitGlobalEnv(thread, globalIndexMap); + globalIndexMap.Update(GlobalIndexMap::GetGlobalIndexMap(thread->GetCurrentEcmaContext())); + EXPECT_EQ(globalIndexMap.GetTaggedValue().IsHeapObject(), true); + auto globalEnv = thread->GetEcmaVM()->GetGlobalEnv(); + uint32_t globalEnvFieldSize = globalEnv->GetGlobalEnvFieldSize(); + for (uint32_t index = 0; index < globalEnvFieldSize; index++) { + JSTaggedValue objectValue = globalEnv->GetGlobalEnvObjectByIndex(index).GetTaggedValue(); + if (objectValue.IsHeapObject()) { + GlobalIndex rootIndex; + GlobalIndexMap::FindGlobalIndex(globalIndexMap, objectValue, &rootIndex); + EXPECT_EQ(static_cast(index), rootIndex.GetGlobalEnvId()); + GlobalIndex globalEnvGlobalIndex; + globalEnvGlobalIndex.UpdateGlobalEnvId(index); + EXPECT_EQ(globalEnvGlobalIndex, rootIndex); + } + } +} + +/** + * @tc.name: InitBuiltinEntries + * @tc.desc: Check whether BuiltinEntries can be find in the GlobalMap. + * @tc.type: FUNC + * @tc.require: + */ +HWTEST_F_L0(GlobalIndexMapTest, GlobalIndexMap_initBuiltinEntries) +{ + JSMutableHandle globalIndexMap( + GlobalIndexMap::GetGlobalIndexMap(thread->GetCurrentEcmaContext()).GetAddress()); + EXPECT_NE(globalIndexMap.GetTaggedValue().IsHeapObject(), true); + GlobalIndexMap::InitGlobalIndexMap(thread, globalIndexMap); + GlobalIndexMap::InitBuiltinEntries(thread, globalIndexMap); + globalIndexMap.Update(GlobalIndexMap::GetGlobalIndexMap(thread->GetCurrentEcmaContext())); + EXPECT_EQ(globalIndexMap.GetTaggedValue().IsHeapObject(), true); + auto builtinEntries = thread->GetBuiltinEntries(); + uint32_t builtinEntriesCount = BuiltinEntries::COUNT; + for (uint32_t index = 0; index < builtinEntriesCount; index++) { + JSTaggedValue objectValue = builtinEntries.builtin_[index].hClass_; + if (objectValue.IsHeapObject()) { + GlobalIndex rootIndex; + GlobalIndexMap::FindGlobalIndex(globalIndexMap, objectValue, &rootIndex); + EXPECT_EQ(static_cast(index), rootIndex.GetBuiltinEntriesId()); + GlobalIndex builtinEntriesIndex; + builtinEntriesIndex.UpdateBuiltinEntriesId(index); + EXPECT_EQ(builtinEntriesIndex, rootIndex); + } + } +} +} // namespace panda::test \ No newline at end of file diff --git a/script/run_ark_executable.py b/script/run_ark_executable.py index c2dc61c5c79ea0efdd0d05542bd79cb19f3b34c5..1012ad29eea940841e898e779fcd300b2fdc1b95 100755 --- a/script/run_ark_executable.py +++ b/script/run_ark_executable.py @@ -132,17 +132,17 @@ def judge_output(args: object): if args.expect_output: returncode = str(subp.returncode) if returncode != args.expect_output: - out_str = out.decode('UTF-8') - err_str = err.decode('UTF-8') + out_str = out.decode('UTF-8', errors="ignore") + err_str = err.decode('UTF-8', errors="ignore") print(out_str) print(err_str) print(">>>>> Expect return: [" + args.expect_output \ + "]\n>>>>> But got: [" + returncode + "]") raise RuntimeError("Run [" + cmd + "] failed!") elif args.expect_sub_output: - out_str = out.decode('UTF-8') + out_str = out.decode('UTF-8', errors="ignore") if out_str.find(args.expect_sub_output) == -1: - out_str = out.decode('UTF-8') + out_str = out.decode('UTF-8', errors="ignore") print(out_str) print(">>>>> Expect contain: [" + args.expect_sub_output \ + "]\n>>>>> But got: [" + out_str + "]") @@ -152,9 +152,9 @@ def judge_output(args: object): # skip license header expect_output = ''.join(file.readlines()[13:]) file.close() - out_str = out.decode('UTF-8') + out_str = out.decode('UTF-8', errors="ignore") if out_str != expect_output: - err_str = err.decode('UTF-8') + err_str = err.decode('UTF-8', errors="ignore") print(err_str) print(">>>>> Expect : [" + expect_output \ + "]\n>>>>> But got: [" + out_str + "]") diff --git a/test/aottest/BUILD.gn b/test/aottest/BUILD.gn index b3050b7ed53a1e604e1297b185954c3bdb816bb8..902882fb668bfe3f711205f465d073330d821223 100644 --- a/test/aottest/BUILD.gn +++ b/test/aottest/BUILD.gn @@ -29,6 +29,7 @@ group("ark_aot_js_test") { "dead_code_elimination", "equal", "js_string_equal", + "js_string_add", ] deps = [] @@ -177,6 +178,7 @@ group("ark_aot_ts_test") { "newobjrange", "newobjspread", "not", + "not_equal", "numberspeculativeretype", "operations_stub_test", "optimization", diff --git a/test/aottest/binaryop_special_value/binaryop_special_value.ts b/test/aottest/binaryop_special_value/binaryop_special_value.ts index 0314c044b6e78766472be5af4127080ac75ea0f5..3d8a9ae6a685ce9e451a3734c9034629b86511b4 100644 --- a/test/aottest/binaryop_special_value/binaryop_special_value.ts +++ b/test/aottest/binaryop_special_value/binaryop_special_value.ts @@ -36,6 +36,7 @@ print(intNum >= undf); print(intNum == undf); print(intNum === undf); print(intNum != undf); +print(intNum !== undf); print(intNum << undf); print(intNum >> undf); print(intNum >>> undf); @@ -56,6 +57,7 @@ print(doubleNum >= undf); print(doubleNum == undf); print(doubleNum === undf); print(doubleNum != undf); +print(doubleNum !== undf); print(doubleNum << undf); print(doubleNum >> undf); print(doubleNum >>> undf); @@ -76,6 +78,7 @@ print(falseValue >= undf); print(falseValue == undf); print(falseValue === undf); print(falseValue != undf); +print(falseValue !== undf); print(falseValue << undf); print(falseValue >> undf); print(falseValue >>> undf); @@ -96,6 +99,7 @@ print(trueValue >= undf); print(trueValue == undf); print(trueValue === undf); print(trueValue != undf); +print(trueValue !== undf); print(trueValue << undf); print(trueValue >> undf); print(trueValue >>> undf); @@ -116,6 +120,7 @@ print(nullValue >= undf); print(nullValue == undf); print(nullValue === undf); print(nullValue != undf); +print(nullValue !== undf); print(nullValue << undf); print(nullValue >> undf); print(nullValue >>> undf); @@ -136,6 +141,7 @@ print(intNum >= nullValue); print(intNum == nullValue); print(intNum === nullValue); print(intNum != nullValue); +print(intNum !== nullValue); print(intNum << nullValue); print(intNum >> nullValue); print(intNum >>> nullValue); @@ -156,6 +162,7 @@ print(doubleNum >= nullValue); print(doubleNum == nullValue); print(doubleNum === nullValue); print(doubleNum != nullValue); +print(doubleNum !== nullValue); print(doubleNum << nullValue); print(doubleNum >> nullValue); print(doubleNum >>> nullValue); @@ -176,6 +183,7 @@ print(falseValue >= nullValue); print(falseValue == nullValue); print(falseValue === nullValue); print(falseValue != nullValue); +print(falseValue !== nullValue); print(falseValue << nullValue); print(falseValue >> nullValue); print(falseValue >>> nullValue); @@ -196,6 +204,7 @@ print(trueValue >= nullValue); print(trueValue == nullValue); print(trueValue === nullValue); print(trueValue != nullValue); +print(trueValue !== nullValue); print(trueValue << nullValue); print(trueValue >> nullValue); print(trueValue >>> nullValue); @@ -216,6 +225,7 @@ print(intNum >= falseValue); print(intNum == falseValue); print(intNum === falseValue); print(intNum != falseValue); +print(intNum !== falseValue); print(intNum << falseValue); print(intNum >> falseValue); print(intNum >>> falseValue); @@ -236,6 +246,7 @@ print(doubleNum >= falseValue); print(doubleNum == falseValue); print(doubleNum === falseValue); print(doubleNum != falseValue); +print(doubleNum !== falseValue); print(doubleNum << falseValue); print(doubleNum >> falseValue); print(doubleNum >>> falseValue); @@ -256,6 +267,7 @@ print(trueValue >= falseValue); print(trueValue == falseValue); print(trueValue === falseValue); print(trueValue != falseValue); +print(trueValue !== falseValue); print(trueValue << falseValue); print(trueValue >> falseValue); print(trueValue >>> falseValue); @@ -276,6 +288,7 @@ print(intNum >= trueValue); print(intNum == trueValue); print(intNum === trueValue); print(intNum != trueValue); +print(intNum !== trueValue); print(intNum << trueValue); print(intNum >> trueValue); print(intNum >>> trueValue); @@ -296,6 +309,7 @@ print(doubleNum >= trueValue); print(doubleNum == trueValue); print(doubleNum === trueValue); print(doubleNum != trueValue); +print(doubleNum !== trueValue); print(doubleNum << trueValue); print(doubleNum >> trueValue); print(doubleNum >>> trueValue); @@ -316,6 +330,7 @@ print(trueValue >= falseValue); print(trueValue == falseValue); print(trueValue === falseValue); print(trueValue != falseValue); +print(trueValue !== falseValue); print(trueValue << falseValue); print(trueValue >> falseValue); print(trueValue >>> falseValue); @@ -336,6 +351,7 @@ print(undf >= undf); print(undf == undf); print(undf === undf); print(undf != undf); +print(undf !== undf); print(undf << undf); print(undf >> undf); print(undf >>> undf); @@ -356,6 +372,7 @@ print(nullValue >= nullValue); print(nullValue == nullValue); print(nullValue === nullValue); print(nullValue != nullValue); +print(nullValue !== nullValue); print(nullValue << nullValue); print(nullValue >> nullValue); print(nullValue >>> nullValue); @@ -376,6 +393,7 @@ print(trueValue >= trueValue); print(trueValue == trueValue); print(trueValue === trueValue); print(trueValue != trueValue); +print(trueValue !== trueValue); print(trueValue << trueValue); print(trueValue >> trueValue); print(trueValue >>> trueValue); @@ -396,6 +414,7 @@ print(falseValue >= falseValue); print(falseValue == falseValue); print(falseValue === falseValue); print(falseValue != falseValue); +print(falseValue !== falseValue); print(falseValue << falseValue); print(falseValue >> falseValue); print(falseValue >>> falseValue); diff --git a/test/aottest/binaryop_special_value/expect_output.txt b/test/aottest/binaryop_special_value/expect_output.txt index d9c4fc4348258d8052ad31b6b92300ab2487ba7f..80806963d858074c3d3be7bc9f9cd632a1974a1d 100644 --- a/test/aottest/binaryop_special_value/expect_output.txt +++ b/test/aottest/binaryop_special_value/expect_output.txt @@ -24,6 +24,7 @@ false false false true +true 5 5 5 @@ -42,6 +43,7 @@ false false false true +true 1 1 1 @@ -60,6 +62,7 @@ false false false true +true 0 0 0 @@ -78,6 +81,7 @@ false false false true +true 1 1 1 @@ -96,6 +100,7 @@ false true false false +true 0 0 0 @@ -114,6 +119,7 @@ true false false true +true 5 5 5 @@ -132,6 +138,7 @@ true false false true +true 1 1 1 @@ -150,6 +157,7 @@ true false false true +true 0 0 0 @@ -168,6 +176,7 @@ true false false true +true 1 1 1 @@ -186,6 +195,7 @@ true false false true +true 5 5 5 @@ -204,6 +214,7 @@ true false false true +true 1 1 1 @@ -222,6 +233,7 @@ true false false true +true 1 1 1 @@ -240,6 +252,7 @@ true false false true +true 10 2 2 @@ -258,6 +271,7 @@ true false false true +true 2 0 0 @@ -276,6 +290,7 @@ true false false true +true 1 1 1 @@ -294,6 +309,7 @@ false true true false +false 0 0 0 @@ -312,6 +328,7 @@ true true true false +false 0 0 0 @@ -330,6 +347,7 @@ true true true false +false 2 0 0 @@ -348,6 +366,7 @@ true true true false +false 0 0 0 diff --git a/test/aottest/exception_case1/expect_output.txt b/test/aottest/exception_case1/expect_output.txt index aef664454297ca65d4891109fa89cc6c9bf765c3..562ec280e5e1c0d3b57a0fd15b2fdead8e142cc5 100644 --- a/test/aottest/exception_case1/expect_output.txt +++ b/test/aottest/exception_case1/expect_output.txt @@ -11,7 +11,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -SyntaxError: Unexpected Array in JSON +SyntaxError: Unexpected Number in JSON Array Or Object exception_case1.js:18:18 exception_case1.js:20:1 diff --git a/test/aottest/exception_case11/expect_output.txt b/test/aottest/exception_case11/expect_output.txt index 866a932dd3b9761950f034653347abcf41564a2c..76321182d6ff72e196e4ad0604be50e362ca942d 100644 --- a/test/aottest/exception_case11/expect_output.txt +++ b/test/aottest/exception_case11/expect_output.txt @@ -11,7 +11,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -SyntaxError: Unexpected Array in JSON +SyntaxError: Unexpected Number in JSON Array Or Object exception_case11.ts:22:22 exception_case11.ts:25:1 diff --git a/test/aottest/exception_case12/expect_output.txt b/test/aottest/exception_case12/expect_output.txt index 7ab54e8b3145c1222154a8b8fd3235054599f4bc..e42f38d42d9548147819629f464d2cc90c5210b2 100644 --- a/test/aottest/exception_case12/expect_output.txt +++ b/test/aottest/exception_case12/expect_output.txt @@ -11,7 +11,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -SyntaxError: Unexpected Array in JSON +SyntaxError: Unexpected Number in JSON Array Or Object exception_case12.ts:21:21 exception_case12.ts:24:1 diff --git a/test/aottest/exception_case8/expect_output.txt b/test/aottest/exception_case8/expect_output.txt index 6e1b8a23ff298a18adeb09d386b8f6bc5112ac22..1908e04e0ade234a9de1ad9a0ea006a6729a4f8f 100644 --- a/test/aottest/exception_case8/expect_output.txt +++ b/test/aottest/exception_case8/expect_output.txt @@ -11,7 +11,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -SyntaxError: Unexpected Array in JSON +SyntaxError: Unexpected Number in JSON Array Or Object exception_case8.js:18:18 exception_case8.js:22:22 exception_case8.js:24:1 diff --git a/test/aottest/instanceof/expect_output.txt b/test/aottest/instanceof/expect_output.txt index bedb3fa1ddd1c0d706901f8083ea60cf5a404a45..0bb3008073871d6d8edc940edccbeabfe13e0a99 100644 --- a/test/aottest/instanceof/expect_output.txt +++ b/test/aottest/instanceof/expect_output.txt @@ -22,6 +22,9 @@ true false true true +false +true +true true true true diff --git a/test/aottest/instanceof/instanceof.ts b/test/aottest/instanceof/instanceof.ts index 7a313e98bc022911ebfb3a6f0cc2f3eaa6613b4f..42e91d58c367717be7a53af37eb3f109f8cbd5e9 100644 --- a/test/aottest/instanceof/instanceof.ts +++ b/test/aottest/instanceof/instanceof.ts @@ -67,6 +67,16 @@ function test5() { } test5(); +function foo() {}; +function bar() {}; +const proxy = new Proxy(foo, {}); + +let f = new foo(); + +print(f instanceof foo); // true +print(f instanceof proxy); // true +print(f instanceof bar); // false + print(ArkTools.isAOTCompiled(test1)); print(ArkTools.isAOTCompiled(test2)); print(ArkTools.isAOTCompiled(test3)); diff --git a/test/aottest/js_string_add/BUILD.gn b/test/aottest/js_string_add/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..4153c71ad0344154f07d3ebc3c2e6753f063a51b --- /dev/null +++ b/test/aottest/js_string_add/BUILD.gn @@ -0,0 +1,19 @@ +# Copyright (c) 2023 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_js_test_action("js_string_add") { + deps = [] + is_enable_pgo = true +} diff --git a/test/aottest/js_string_add/expect_output.txt b/test/aottest/js_string_add/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..9b05ecd62e51ac9e2a3e9ba3ad28fa9a731ca693 --- /dev/null +++ b/test/aottest/js_string_add/expect_output.txt @@ -0,0 +1,23 @@ +# Copyright (c) 2023 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. + +HelloWorld +Hello WorldHello +123 +Hello, OD +Hello, Huawei +Hello, +BUGBUG +BUGfull of bug +full of bugfull of bug +%F0 diff --git a/test/aottest/js_string_add/js_string_add.js b/test/aottest/js_string_add/js_string_add.js new file mode 100644 index 0000000000000000000000000000000000000000..c2b9aff45b1e7ec5ccd3591241682aab6b1e5470 --- /dev/null +++ b/test/aottest/js_string_add/js_string_add.js @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2023 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. + */ + +let str = "Hello World"; + +// two const +print("Hello" + "World"); +print(str + "Hello"); +print("" + "" + "123" + ""); + +// one const +let strs = ["OD", "Huawei", ""]; +for (let i = 0; i<3; ++i) { + let m = strs[i]; + print("Hello, " + m); +} + +// no const +function foo(flag) { + let str = ["BUG", "full of bug"]; + if (flag) { + return str[0]; + } else { + return str[1]; + } +} +let left = foo(true); +let right1 = foo(true); +let right2 = foo(false); +print(left + right1); +print(left + right2); +print(right2 + right2); + +var hex = "0123456789ABCDEF"; +print("%" + hex[(0xF0 >> 4) & 0xf] + hex[0xF0 & 0xf]); \ No newline at end of file diff --git a/test/aottest/js_string_add/pgo_expect_output.txt b/test/aottest/js_string_add/pgo_expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..9b05ecd62e51ac9e2a3e9ba3ad28fa9a731ca693 --- /dev/null +++ b/test/aottest/js_string_add/pgo_expect_output.txt @@ -0,0 +1,23 @@ +# Copyright (c) 2023 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. + +HelloWorld +Hello WorldHello +123 +Hello, OD +Hello, Huawei +Hello, +BUGBUG +BUGfull of bug +full of bugfull of bug +%F0 diff --git a/test/aottest/js_string_equal/BUILD.gn b/test/aottest/js_string_equal/BUILD.gn index 6ecf3f70f5f9a2700778622e58e97537d949f3c3..801765f9cf353eb3e9227e69a0fb1adbb7ed1a60 100644 --- a/test/aottest/js_string_equal/BUILD.gn +++ b/test/aottest/js_string_equal/BUILD.gn @@ -15,4 +15,5 @@ import("//arkcompiler/ets_runtime/test/test_helper.gni") host_aot_js_test_action("js_string_equal") { deps = [] + is_enable_pgo = true } diff --git a/test/aottest/js_string_equal/pgo_expect_output.txt b/test/aottest/js_string_equal/pgo_expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..a51946ab5198a84a707e64c9f066f8d59c673ca7 --- /dev/null +++ b/test/aottest/js_string_equal/pgo_expect_output.txt @@ -0,0 +1,21 @@ +# Copyright (c) 2023 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. + +true +true +false +false +true +false +false +true diff --git a/test/moduletest/arrayflatmap/BUILD.gn b/test/aottest/not_equal/BUILD.gn similarity index 94% rename from test/moduletest/arrayflatmap/BUILD.gn rename to test/aottest/not_equal/BUILD.gn index 1bcef0c2c3505d1dfb7039caef517fd5705921ea..1c035d43b8a200af41fc70a2c41a49ea00d88ace 100644 --- a/test/moduletest/arrayflatmap/BUILD.gn +++ b/test/aottest/not_equal/BUILD.gn @@ -13,6 +13,6 @@ import("//arkcompiler/ets_runtime/test/test_helper.gni") -host_moduletest_action("arrayflatmap") { +host_aot_js_test_action("not_equal") { deps = [] } diff --git a/test/aottest/not_equal/expect_output.txt b/test/aottest/not_equal/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..866e77b029cc4dae573ced16f4435f59f1619ee5 --- /dev/null +++ b/test/aottest/not_equal/expect_output.txt @@ -0,0 +1,37 @@ +# Copyright (c) 2023 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. + +true +true +true +true +false +false +false +false +false +false +false +false +true +true +true +true +false +false +true +true +true +true +false +false diff --git a/test/aottest/not_equal/not_equal.js b/test/aottest/not_equal/not_equal.js new file mode 100644 index 0000000000000000000000000000000000000000..e81d3634f8cb60a7ddb2ff4a071a931ade449c19 --- /dev/null +++ b/test/aottest/not_equal/not_equal.js @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2023 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. + */ + +var a = 2; +print(a != null); +print(null != a); +print(a != undefined); +print(undefined != a); + +var b = null; +print(b != null); +print(null != b); +print(b != undefined); +print(undefined != b); + +var c = undefined; +print(c != null); +print(null != c); +print(c != undefined); +print(undefined != c); + +var d = 2; +print(d !== null); +print(null !== d); +print(d !== undefined); +print(undefined !== d); + +var e = null; +print(e !== null); +print(null !== e); +print(e !== undefined); +print(undefined !== e); + +var f = undefined; +print(f !== null); +print(null !== f); +print(f !== undefined); +print(undefined !== f); \ No newline at end of file diff --git a/test/aottest/ts_inline_exception3/expect_output.txt b/test/aottest/ts_inline_exception3/expect_output.txt index ab3ea753057ca43c290cea4655e03a59d50491b9..c884d9eb72f22bb7c9445e59ed1d21c7d71ab3b3 100644 --- a/test/aottest/ts_inline_exception3/expect_output.txt +++ b/test/aottest/ts_inline_exception3/expect_output.txt @@ -11,7 +11,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -SyntaxError: Unexpected Array in JSON +SyntaxError: Unexpected Number in JSON Array Or Object at foo1 (maybe inlined). depth: 2 at foo3 (maybe inlined). depth: 1 at foo4 (maybe inlined). depth: 0 diff --git a/test/aottest/ts_inline_exception7/expect_output.txt b/test/aottest/ts_inline_exception7/expect_output.txt index 8ebf2e2d6fdcf17b6a85b763e5443808434b6ed8..b880a9c4fce34c349dc81d6078bcaaa02cccaa43 100644 --- a/test/aottest/ts_inline_exception7/expect_output.txt +++ b/test/aottest/ts_inline_exception7/expect_output.txt @@ -11,7 +11,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -SyntaxError: Unexpected Array in JSON +SyntaxError: Unexpected Number in JSON Array Or Object at Student (hidden:26:26) at foo1 (maybe inlined). depth: 1 at foo2 (maybe inlined). depth: 0 diff --git a/test/moduletest/BUILD.gn b/test/moduletest/BUILD.gn index d251202364d1c8dad5ce7aae1d47b2f5328faf90..b6a95f05d3a7482fc2efece52ab8537dba6fbe9c 100644 --- a/test/moduletest/BUILD.gn +++ b/test/moduletest/BUILD.gn @@ -20,11 +20,11 @@ group("ark_js_moduletest") { "array", "arrayfindlast", "arrayflat", - "arrayflatmap", "arrayfindindex", "arrayfindlastindex", "arrayforeach", "arrayjoin", + "arraymap", "arraypop", "arraytoreversed", "arraytospliced", @@ -113,6 +113,7 @@ group("ark_js_moduletest") { "regexpcallthrow", "regexpflagd", "regress", + "regressdefineproperty", "require", "setobjectwithproto", "spreadoperator", @@ -176,11 +177,11 @@ group("ark_asm_test") { "array", "arrayfindlast", "arrayflat", - "arrayflatmap", "arrayfindlastindex", "arrayfindindex", "arrayforeach", "arrayjoin", + "arraymap", "arraypop", "arraysort", "arrayprotochange", @@ -309,10 +310,10 @@ group("ark_asm_single_step_test") { "arrayfindindex", "arrayfindlast", "arrayflat", - "arrayflatmap", "arrayfindlastindex", "arrayforeach", "arrayjoin", + "arraymap", "arraypop", "arrayprotochange", "arrayshift", diff --git a/test/moduletest/arrayConcat/BUILD.gn b/test/moduletest/arrayConcat/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..6534cb435800be36b71616c71922a2a94c3b3f37 --- /dev/null +++ b/test/moduletest/arrayConcat/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2023 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_moduletest_action("arrayConcat") { + deps = [] +} diff --git a/test/moduletest/arrayConcat/arrayConcat.js b/test/moduletest/arrayConcat/arrayConcat.js new file mode 100644 index 0000000000000000000000000000000000000000..377a03e14449691b4983e380162229270742a381 --- /dev/null +++ b/test/moduletest/arrayConcat/arrayConcat.js @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const array1 = ['a', 'b', 'c']; +const array2 = ['d', 'e', 'f']; +const array3 = array1.concat(array2); +print(array3); + +const letters = ["a", "b", "c"]; +const numbers = [1, 2, 3]; + +const alphaNumeric = letters.concat(numbers); +print(alphaNumeric); + +const num1 = [1, 2, 3]; +const num2 = [4, 5, 6]; +const num3 = [7, 8, 9]; + +const numbers1 = num1.concat(num2, num3); + +print(numbers1); + + +const letters1 = ["a", "b", "c"]; + +const alphaNumeric1 = letters1.concat(1, [2, 3]); + +print(alphaNumeric1); + +const num11 = [[1]]; +const num22 = [2, [3]]; + +const numbers2 = num1.concat(num22); + +print(numbers2); +// [[1], 2, [3]] + +num11[0].push(4); + +print(numbers2); + diff --git a/test/moduletest/arrayConcat/expect_output.txt b/test/moduletest/arrayConcat/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..4139bcfab655663d85858b1cc90c8f224dd4904a --- /dev/null +++ b/test/moduletest/arrayConcat/expect_output.txt @@ -0,0 +1,19 @@ +# Copyright (c) 2023 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. + +a,b,c,d,e,f +a,b,c,1,2,3 +1,2,3,4,5,6,7,8,9 +a,b,c,1,2,3 +1,2,3,2,3 +1,2,3,2,3 diff --git a/test/moduletest/arrayFindIndexCase/BUILD.gn b/test/moduletest/arrayFindIndexCase/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..db14fd79d8b71e7e71c672bb6f2f67a1705d1cca --- /dev/null +++ b/test/moduletest/arrayFindIndexCase/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2023 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_moduletest_action("findIndex") { + deps = [] +} diff --git a/test/moduletest/arrayflatmap/expect_output.txt b/test/moduletest/arrayFindIndexCase/expect_output.txt similarity index 98% rename from test/moduletest/arrayflatmap/expect_output.txt rename to test/moduletest/arrayFindIndexCase/expect_output.txt index ce415d321ab7b920f7a85495b54d7f2a762720f0..eb22b1c779fc64ec1f114dfa56c94d9b014206a0 100644 --- a/test/moduletest/arrayflatmap/expect_output.txt +++ b/test/moduletest/arrayFindIndexCase/expect_output.txt @@ -11,4 +11,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -true +3 +1 +1 +-1 +2 diff --git a/test/moduletest/arrayFindIndexCase/findIndex.js b/test/moduletest/arrayFindIndexCase/findIndex.js new file mode 100644 index 0000000000000000000000000000000000000000..0fc4ffd389111932b065752fffba07aa41846c2e --- /dev/null +++ b/test/moduletest/arrayFindIndexCase/findIndex.js @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2023 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. + */ + +/* + * @tc.name:findIndex + * @tc.desc:test array.findIndex + * @tc.type: FUNC + * @tc.require: + */ + +const array1 = [5, 12, 8, 130, 44]; +const isLargeNumber = (element) => element > 13; +print(array1.findIndex(isLargeNumber)); + +const arrayLike = { + length: 3, + 0: 2, + 1: 7.3, + 2: 4, +}; +print( + Array.prototype.findIndex.call(arrayLike, (x) => !Number.isInteger(x)), +); +print([1, , 3].findIndex((x) => x === undefined)); +function isPrime(element) { + if (element % 2 === 0 || element < 2) { + return false; + } + for (let factor = 3; factor <= Math.sqrt(element); factor += 2) { + if (element % factor === 0) { + return false; + } + } + return true; +} +print([4, 6, 8, 9, 12].findIndex(isPrime)); +print([4, 6, 7, 9, 12].findIndex(isPrime)); diff --git a/test/moduletest/arrayReverseCase/BUILD.gn b/test/moduletest/arrayReverseCase/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..471ede6d01037dff81bf8c5385e88ceb87f8b2e2 --- /dev/null +++ b/test/moduletest/arrayReverseCase/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_moduletest_action("arrayReverseCase") { + deps = [] +} diff --git a/test/moduletest/arrayReverseCase/arrayReverseCase.js b/test/moduletest/arrayReverseCase/arrayReverseCase.js new file mode 100644 index 0000000000000000000000000000000000000000..1768080e2e63968c22adba5d5b5aa1c5af4b7db8 --- /dev/null +++ b/test/moduletest/arrayReverseCase/arrayReverseCase.js @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const array1 = ['one', 'two', 'three']; +print('array1:', array1); +// Expected output: "array1:" Array ["one", "two", "three"] + +const reversed1 = array1.reverse(); +print('reversed1:', reversed1); +// Expected output: "reversed1:" Array ["three", "two", "one"] + +// Careful: reverse is destructive -- it changes the original array. +print('array1:', array1); +// Expected output: "array1:" Array ["three", "two", "one"] + +print([1, , 3].reverse()); // [3, empty, 1] +print([1, , 3, 4].reverse()); // [4, 3, empty, 1] + +const numbers = [3, 2, 4, 1, 5]; +// [...numbers] 创建一个浅拷贝,因此 reverse() 不会改变原始数据 +const reverted = [...numbers].reverse(); +reverted[0] = 5; +print(numbers[0]); // 3 + +const numbers1 = [3, 2, 4, 1, 5]; +const reversed = numbers1.reverse(); +// numbers1 和 reversed 的顺序都是颠倒的 [5, 1, 4, 2, 3] +reversed[0] = 5; +print(numbers1[0]); // 5 diff --git a/test/moduletest/arrayReverseCase/expect_output.txt b/test/moduletest/arrayReverseCase/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..4a1b9fffd943bc681994711447ab59e7df0a3e0a --- /dev/null +++ b/test/moduletest/arrayReverseCase/expect_output.txt @@ -0,0 +1,20 @@ +# Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development 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. + +array1: one,two,three +reversed1: three,two,one +array1: three,two,one +3,,1 +4,3,,1 +3 +5 diff --git a/test/moduletest/arraySliceCase/BUILD.gn b/test/moduletest/arraySliceCase/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..aa7aac74627a0b4f3cb45c44ddf9f4c4db11fa3f --- /dev/null +++ b/test/moduletest/arraySliceCase/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2023 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_moduletest_action("arraySliceCase") { + deps = [] +} diff --git a/test/moduletest/arraySliceCase/arraySliceCase.js b/test/moduletest/arraySliceCase/arraySliceCase.js new file mode 100644 index 0000000000000000000000000000000000000000..b2faf7506001a326c2ce18d1b0a64e0c57a2f917 --- /dev/null +++ b/test/moduletest/arraySliceCase/arraySliceCase.js @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 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. + */ + +/* + * @tc.name:arraySlice + * @tc.desc:test array.slice + * @tc.type: FUNC + * @tc.require: + */ + +const animals = ['ant', 'bison', 'camel', 'duck', 'elephant']; + +print(animals.slice(2)); +// Expected output: Array ["camel", "duck", "elephant"] + +print(animals.slice(2, 4)); +// Expected output: Array ["camel", "duck"] + +print(animals.slice(1, 5)); +// Expected output: Array ["bison", "camel", "duck", "elephant"] + +print(animals.slice(-2)); +// Expected output: Array ["duck", "elephant"] + +print(animals.slice(2, -1)); +// Expected output: Array ["camel", "duck"] + +print(animals.slice()); +// Expected output: Array ["ant", "bison", "camel", "duck", "elephant"] + +print([1, 2, , 4, 5].slice(1, 4)); // [2, empty, 4] +const arrayLike = { + length: 3, + 0: 2, + 1: 3, + 2: 4, +}; +print(Array.prototype.slice.call(arrayLike, 1, 3)); + +const slice = Function.prototype.call.bind(Array.prototype.slice); + +function list() { + return slice(arguments); +} + +const list1 = list(1, 2, 3); // [1, 2, 3] + +print(list1); diff --git a/test/moduletest/arraySliceCase/expect_output.txt b/test/moduletest/arraySliceCase/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..41082dd1bd507ace9b0e62914f7c1eff8b26c981 --- /dev/null +++ b/test/moduletest/arraySliceCase/expect_output.txt @@ -0,0 +1,22 @@ +# Copyright (c) 2023 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. + +camel,duck,elephant +camel,duck +bison,camel,duck,elephant +duck,elephant +camel,duck +ant,bison,camel,duck,elephant +2,,4 +3,4 +1,2,3 diff --git a/test/moduletest/arrayValuesCase/BUILD.gn b/test/moduletest/arrayValuesCase/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..2509f350b4f44d38af0ad99a2af4aa937622751a --- /dev/null +++ b/test/moduletest/arrayValuesCase/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2023 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_moduletest_action("arrayValuesCase") { + deps = [] +} diff --git a/test/moduletest/arrayValuesCase/arrayValuesCase.js b/test/moduletest/arrayValuesCase/arrayValuesCase.js new file mode 100644 index 0000000000000000000000000000000000000000..83f60c4464d2f23449e14c964dfb3010ae34f914 --- /dev/null +++ b/test/moduletest/arrayValuesCase/arrayValuesCase.js @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2023 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. + */ + +/* + * @tc.name:stringSlice + * @tc.desc:test String.slice + * @tc.type: FUNC + * @tc.require: issueI5NO8G + */ + + +const array1 = ['a', 'b', 'c']; +const iterator1 = array1.values(); + +for (const value of iterator1) { + print(value); +} +const arrayLike = { + length: 3, + 0: "a", + 1: "b", + 2: "c", +}; +for (const entry of Array.prototype.values.call(arrayLike)) { + print(entry); +} +for (const element of [, "a"].values()) { + print(element); +} +const arr = ["a", "b", "c", "d", "e"]; +const iterator = arr.values(); +print(iterator.next().value); // "a" +arr[1] = "n"; +print(iterator.next().value); // "n" +const array = ["a", "b", "c", "d", "e"]; +const values = array.values(); +for (const letter of values) { + print(letter); + if (letter === "b") { + break; + } +} +for (const letter of values) { + print(letter); +} + +const arr1 = ["a", "b", "c", "d", "e"]; +const values1 = arr1.values(); +for (const letter of values1) { + print(letter); +} +for (const letter of values1) { + print(letter); +} diff --git a/test/moduletest/arrayValuesCase/expect_output.txt b/test/moduletest/arrayValuesCase/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..5bf699df0cac6ccebafa062a3700007e361d2daa --- /dev/null +++ b/test/moduletest/arrayValuesCase/expect_output.txt @@ -0,0 +1,33 @@ +# Copyright (c) 2023 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. + +a +b +c +a +b +c +undefined +a +a +n +a +b +c +d +e +a +b +c +d +e diff --git a/test/moduletest/arrayfind/BUILD.gn b/test/moduletest/arrayfind/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..004ca2e948ee1fd7657183ed1fda678bb1353b78 --- /dev/null +++ b/test/moduletest/arrayfind/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2023 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_moduletest_action("arrayfind") { + deps = [] +} diff --git a/test/moduletest/arrayfind/arrayfind.js b/test/moduletest/arrayfind/arrayfind.js new file mode 100644 index 0000000000000000000000000000000000000000..4d4fdb28572392e1d2b2691ffe758fb115021d58 --- /dev/null +++ b/test/moduletest/arrayfind/arrayfind.js @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2023 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. + */ + +/* + * @tc.name:stringSlice + * @tc.desc:test String.slice + * @tc.type: FUNC + * @tc.require: issueI5NO8G + */ + +const array1 = [5, 12, 8, 130, 44]; +const found = array1.find((element) => element > 10); +console.log(found); + +const arrayLike = { + length: 3, + 0: 2, + 1: 7.3, + 2: 4, +}; +console.log(Array.prototype.find.call(arrayLike, (x) => !Number.isInteger(x))); + +const array = [0, 1, , , , 5, 6]; + +array.find((value, index) => { + console.log(`${index},${value}`); +}); + +array.find((value, index) => { + if (index === 0) { + console.log(`array[5]${array[5]}`); + delete array[5]; + } + console.log(`${index},${value}`); +}); + +function isPrime(element, index, array) { + let start = 2; + while (start <= Math.sqrt(element)) { + if (element % start++ < 1) { + return false; + } + } + return element > 1; +} + +console.log([4, 6, 8, 12].find(isPrime)); // undefined,未找到 +console.log([4, 5, 8, 12].find(isPrime)); // 5 + diff --git a/test/moduletest/arrayfind/expect_output.txt b/test/moduletest/arrayfind/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..6bbc994cc10a52732f112968016fe005f5206af5 --- /dev/null +++ b/test/moduletest/arrayfind/expect_output.txt @@ -0,0 +1,32 @@ +# Copyright (c) 2023 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. + +12 +7.3 +0,0 +1,1 +2,undefined +3,undefined +4,undefined +5,5 +6,6 +array[5]5 +0,0 +1,1 +2,undefined +3,undefined +4,undefined +5,undefined +6,6 +undefined +5 diff --git a/test/moduletest/arrayflat/arrayflat.js b/test/moduletest/arrayflat/arrayflat.js index ad8dc59334ddf553250468f952d1f5b3068e3e01..22fb4c7cbb02eb0b0eeeca9b13f7cce3cb1fe091 100644 --- a/test/moduletest/arrayflat/arrayflat.js +++ b/test/moduletest/arrayflat/arrayflat.js @@ -22,13 +22,3 @@ const input = [1, [2], [[3]]]; print(input.flat(undefined)); -{ - class MyArray extends Array { - static get [Symbol.species]() { - return this; - } - } - const wannabe = new MyArray(); - const flattened = wannabe.flat(Infinity); - print(flattened instanceof MyArray); -} diff --git a/test/moduletest/arrayflat/expect_output.txt b/test/moduletest/arrayflat/expect_output.txt index 53aae57749cf1f57483fe8248ba86fee0dc95522..c2f8381c3b061f402b51849c8bd6c7d55d12b6ea 100644 --- a/test/moduletest/arrayflat/expect_output.txt +++ b/test/moduletest/arrayflat/expect_output.txt @@ -12,4 +12,3 @@ # limitations under the License. 1,2,3 -true diff --git a/test/moduletest/arraymap/BUILD.gn b/test/moduletest/arraymap/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..e48a6ae56308178588b0c4e04852233ed1c602b8 --- /dev/null +++ b/test/moduletest/arraymap/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2023 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_moduletest_action("arraymap") { + deps = [] +} diff --git a/test/moduletest/arrayflatmap/arrayflatmap.js b/test/moduletest/arraymap/arraymap.js similarity index 68% rename from test/moduletest/arrayflatmap/arrayflatmap.js rename to test/moduletest/arraymap/arraymap.js index 9a27ec63b496192a5ac98e878b754688750f918d..eb83050db7bb279ecf79a6a2ab39f66f076c152b 100644 --- a/test/moduletest/arrayflatmap/arrayflatmap.js +++ b/test/moduletest/arraymap/arraymap.js @@ -15,17 +15,15 @@ /* * @tc.name:async - * @tc.desc:array.flatmap + * @tc.desc:test async function * @tc.type: FUNC - * @tc.require:issueI8FBM3 + * @tc.require: issueI5NO8G issueI8FBM3 */ -{ - class MyArray extends Array { - static get [Symbol.species]() { - return this; - } - } - const wannabe = new MyArray(); - const result = wannabe.flatMap(x => [x, x]); - print(result instanceof MyArray);//t +var array = [,]; +function map() { + return array.map(x => x + 1); } +array.__proto__.push(5); +var narr = map(); +print(JSON.stringify(Object.getOwnPropertyDescriptor(narr, 0))); +print(narr[0], 6); \ No newline at end of file diff --git a/test/moduletest/arraymap/expect_output.txt b/test/moduletest/arraymap/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..d4969415c52e2d69ab9dc262604ce0b998ee4588 --- /dev/null +++ b/test/moduletest/arraymap/expect_output.txt @@ -0,0 +1,15 @@ +# Copyright (c) 2023 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. + +{"value":6,"writable":true,"enumerable":true,"configurable":true} +6 6 diff --git a/test/moduletest/arraypop/arraypop.js b/test/moduletest/arraypop/arraypop.js index 095bdcba868a0194d5d7dd83f22d99c564bde268..09bd789bea716fd31cccdd728d783ceb2e65feb9 100644 --- a/test/moduletest/arraypop/arraypop.js +++ b/test/moduletest/arraypop/arraypop.js @@ -27,4 +27,4 @@ } array.__proto__.push(6); print(pop()); -})(); \ No newline at end of file +})(); diff --git a/test/moduletest/arrayslice/arrayslice.js b/test/moduletest/arrayslice/arrayslice.js index 9c099983ca5e85c277afe954ef02b1484d10648b..5492dfb84aa345785d2bc9fcf84126968c620604 100644 --- a/test/moduletest/arrayslice/arrayslice.js +++ b/test/moduletest/arrayslice/arrayslice.js @@ -12,6 +12,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +var holey_array = [1, 2, 3, 4, 5,,,,,,]; +print(holey_array.slice(6, 7)[0]); +print(holey_array.slice(2, 3)[0]); (function() { var array = [,]; diff --git a/test/moduletest/arrayslice/expect_output.txt b/test/moduletest/arrayslice/expect_output.txt index 6a9bc8b453bfda1e144123ae6e419a6cfa65dbbe..c5b432b3e621d09515c37f35d107ec61ec04aacf 100644 --- a/test/moduletest/arrayslice/expect_output.txt +++ b/test/moduletest/arrayslice/expect_output.txt @@ -11,4 +11,6 @@ # See the License for the specific language governing permissions and # limitations under the License. +undefined +3 [object Object] diff --git a/test/moduletest/regress/expect_output.txt b/test/moduletest/regress/expect_output.txt index 1fe1378d296e5ad3fc0d17d504796fd91e8898e6..2383c312e23417b2d8c75ff010d28e16bb87501b 100755 --- a/test/moduletest/regress/expect_output.txt +++ b/test/moduletest/regress/expect_output.txt @@ -132,3 +132,12 @@ true true true true +true +true +true +true +true +true +true +true +true diff --git a/test/moduletest/regress/regress.js b/test/moduletest/regress/regress.js index 8b583e078175c6c892af506d4ec034746454d612..7dbfedbb48bd6e04215bdf35361b0694a30e9360 100755 --- a/test/moduletest/regress/regress.js +++ b/test/moduletest/regress/regress.js @@ -304,6 +304,16 @@ print(1283261736000 == Date.parse("2010-08-31T22:35:36+0900")); print(Math.pow(2, 31) == foo(Math.pow(2, 31) - 1)); })(); + // mjsunit/regress/regress-12256.js + const datesList = [{ year: '2021', month: '10', day: '22', hour: '10', minute: '12', second: '32' }, + { year: '2021', month: '8', day: '3', hour: '9', minute: '9', second: '6' }]; + const { year, month, day, hour, minute, second } = datesList[0]; + const s0 = `${year}-${month}-${day} ${hour}:${minute}:${second}Z`; + for (let i = 1; i < 10; i++) { + const s1 = `${'0'.repeat(i) + year}-${month}-${day} ${hour}:${minute}:${second}Z`; + print(new Date(s0).getTime() == new Date(s1).getTime()); + } + // mjsunit/regress/regress-crbug-1262007.js function foo(...args) { class C {} @@ -315,4 +325,4 @@ print(1283261736000 == Date.parse("2010-08-31T22:35:36+0900")); foo() } catch(e) { print(e instanceof TypeError) - } \ No newline at end of file + } diff --git a/test/moduletest/regressdefineproperty/BUILD.gn b/test/moduletest/regressdefineproperty/BUILD.gn new file mode 100755 index 0000000000000000000000000000000000000000..99fe717091842e5988e5a1a95d7fc4239e833275 --- /dev/null +++ b/test/moduletest/regressdefineproperty/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2023 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_moduletest_action("regressdefineproperty") { + deps = [] +} diff --git a/test/moduletest/regressdefineproperty/expect_output.txt b/test/moduletest/regressdefineproperty/expect_output.txt new file mode 100755 index 0000000000000000000000000000000000000000..9dc1ee02d85ab04bd92433cd203c3c39c84e7673 --- /dev/null +++ b/test/moduletest/regressdefineproperty/expect_output.txt @@ -0,0 +1,14 @@ +# Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development 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. + +true diff --git a/test/moduletest/regressdefineproperty/regressdefineproperty.js b/test/moduletest/regressdefineproperty/regressdefineproperty.js new file mode 100755 index 0000000000000000000000000000000000000000..9150337a2fb9f8a3d52b3b30a4fe3f3744a90eb9 --- /dev/null +++ b/test/moduletest/regressdefineproperty/regressdefineproperty.js @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023 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. + */ +let a = []; +Object.defineProperty(a, "length", {writable: false}); +function f() { + return a.pop(); +} +try{ + f(); +} catch (e) { + print(e instanceof TypeError) +}