/* * Copyright (c) 2021 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ecmascript/base/error_helper.h" #include "ecmascript/base/builtins_base.h" #include "ecmascript/base/error_type.h" #include "ecmascript/ecma_macros.h" #include "ecmascript/ecma_vm.h" #include "ecmascript/global_env.h" #include "ecmascript/interpreter/frame_handler.h" #include "ecmascript/interpreter/interpreter.h" #include "ecmascript/js_object-inl.h" #include "ecmascript/js_tagged_value-inl.h" #include "ecmascript/jspandafile/js_pandafile_manager.h" #include "ecmascript/object_factory.h" #include "ecmascript/tooling/backend/js_pt_extractor.h" namespace panda::ecmascript::base { using panda::ecmascript::tooling::JSPtExtractor; JSTaggedValue ErrorHelper::ErrorCommonToString(EcmaRuntimeCallInfo *argv, const ErrorType &errorType) { ASSERT(argv); JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); // 1. Let O be the this value. // 2. If Type(O) is not Object, throw a TypeError exception JSHandle thisValue = BuiltinsBase::GetThis(argv); if (!thisValue->IsECMAObject()) { THROW_TYPE_ERROR_AND_RETURN(thread, "ErrorToString:not an object", JSTaggedValue::Exception()); } // 3. Let name be Get(O, "name"). // 4. ReturnIfAbrupt(name). auto globalConst = thread->GlobalConstants(); JSHandle handleName = globalConst->GetHandledNameString(); JSHandle name = JSObject::GetProperty(thread, thisValue, handleName).GetValue(); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // 5. If name is undefined, let name be "Error"; otherwise let name be ToString(name). // 6. ReturnIfAbrupt(name). name = ErrorHelper::GetErrorName(thread, name, errorType); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // 7. Let msg be Get(O, "message"). // 8. ReturnIfAbrupt(msg). JSHandle handleMsg = globalConst->GetHandledMessageString(); JSHandle msg = JSObject::GetProperty(thread, thisValue, handleMsg).GetValue(); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // 9. If msg is undefined, let msg be the empty String; otherwise let msg be ToString(msg). // 10. ReturnIfAbrupt(msg). ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); if (msg->IsUndefined()) { msg = JSHandle::Cast(factory->GetEmptyString()); } else { msg = JSHandle::Cast(JSTaggedValue::ToString(thread, msg)); } RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // 11. If name is the empty String, return msg. // 12. If msg is the empty String, return name. if (JSHandle::Cast(name)->GetLength() == 0) { return msg.GetTaggedValue(); } if (JSHandle::Cast(msg)->GetLength() == 0) { return name.GetTaggedValue(); } // 13. Return the result of concatenating name, the code unit 0x003A (COLON), the code unit 0x0020 (SPACE), and msg. JSHandle space = factory->NewFromCanBeCompressString(": "); JSHandle jsHandleName = JSHandle::Cast(name); JSHandle jsHandleMsg = JSHandle::Cast(msg); JSHandle handleNameSpace = factory->ConcatFromString(jsHandleName, space); JSHandle result = factory->ConcatFromString(handleNameSpace, jsHandleMsg); return result.GetTaggedValue(); } JSHandle ErrorHelper::GetErrorName(JSThread *thread, const JSHandle &name, const ErrorType &errorType) { auto globalConst = thread->GlobalConstants(); if (name->IsUndefined()) { TaggedObject *errorKey = nullptr; switch (errorType) { case ErrorType::RANGE_ERROR: errorKey = reinterpret_cast(*globalConst->GetHandledRangeErrorString()); break; case ErrorType::EVAL_ERROR: errorKey = reinterpret_cast(*globalConst->GetHandledEvalErrorString()); break; case ErrorType::REFERENCE_ERROR: errorKey = reinterpret_cast(*globalConst->GetHandledReferenceErrorString()); break; case ErrorType::TYPE_ERROR: errorKey = reinterpret_cast(*globalConst->GetHandledTypeErrorString()); break; case ErrorType::URI_ERROR: errorKey = reinterpret_cast(*globalConst->GetHandledURIErrorString()); break; case ErrorType::SYNTAX_ERROR: errorKey = reinterpret_cast(*globalConst->GetHandledSyntaxErrorString()); break; default: errorKey = reinterpret_cast(*globalConst->GetHandledErrorString()); break; } return JSHandle(thread, JSTaggedValue(errorKey)); } return JSHandle::Cast(JSTaggedValue::ToString(thread, name)); } JSTaggedValue ErrorHelper::ErrorCommonConstructor(EcmaRuntimeCallInfo *argv, [[maybe_unused]] const ErrorType &errorType) { JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); // 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget. auto ecmaVm = thread->GetEcmaVM(); ObjectFactory *factory = ecmaVm->GetFactory(); JSHandle ctor = BuiltinsBase::GetConstructor(argv); JSMutableHandle newTarget(BuiltinsBase::GetNewTarget(argv)); if (newTarget->IsUndefined()) { newTarget.Update(ctor.GetTaggedValue()); } JSHandle message = BuiltinsBase::GetCallArg(argv, 0); // 2. Let O be OrdinaryCreateFromConstructor(newTarget, "%ErrorPrototype%", «[[ErrorData]]»). JSHandle nativeInstanceObj = factory->NewJSObjectByConstructor(JSHandle(ctor), newTarget); // 3. ReturnIfAbrupt(O). RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // 4. If message is not undefined, then // a. Let msg be ToString(message). // b. ReturnIfAbrupt(msg). // c. Let msgDesc be the PropertyDescriptor{[[Value]]: msg, [[Writable]]: true, [[Enumerable]]: false, // [[Configurable]]: true}. // d. Let status be DefinePropertyOrThrow(O, "message", msgDesc). // e. Assert: status is not an abrupt completion auto globalConst = thread->GlobalConstants(); if (!message->IsUndefined()) { JSHandle handleStr = JSTaggedValue::ToString(thread, message); LOG(DEBUG, ECMASCRIPT) << "Ark throw error: " << utf::Mutf8AsCString(handleStr->GetDataUtf8()); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle msgKey = globalConst->GetHandledMessageString(); PropertyDescriptor msgDesc(thread, JSHandle::Cast(handleStr), true, false, true); [[maybe_unused]] bool status = JSObject::DefineOwnProperty(thread, nativeInstanceObj, msgKey, msgDesc); ASSERT_PRINT(status == true, "return result exception!"); } 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); ASSERT_PRINT(status == true, "return result exception!"); // 5. Return O. return nativeInstanceObj.GetTaggedValue(); } std::string ErrorHelper::DecodeFunctionName(const std::string &name) { if (name.empty()) { return "anonymous"; } return name; } JSHandle ErrorHelper::BuildEcmaStackTrace(JSThread *thread) { std::string data = BuildNativeEcmaStackTrace(thread); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); LOG(DEBUG, ECMASCRIPT) << data; return factory->NewFromStdString(data); } std::string ErrorHelper::BuildNativeEcmaStackTrace(JSThread *thread) { std::string data; auto sp = const_cast(thread->GetCurrentSPFrame()); InterpretedFrameHandler frameHandler(sp); for (; frameHandler.HasFrame(); frameHandler.PrevInterpretedFrame()) { if (frameHandler.IsBreakFrame()) { continue; } auto method = frameHandler.GetMethod(); if (! method->IsNative()) { data.append(" at "); data += DecodeFunctionName(method->ParseFunctionName()); data.append(" ("); // source file tooling::JSPtExtractor *debugExtractor = JSPandaFileManager::GetInstance()->GetJSPtExtractor(method->GetJSPandaFile()); const std::string &sourceFile = debugExtractor->GetSourceFile(method->GetFileId()); if (sourceFile.empty()) { data.push_back('?'); } else { data += sourceFile; } data.push_back(':'); // line number and column number auto callbackLineFunc = [&data](int32_t line) -> bool { data += ToCString(line + 1); data.push_back(':'); return true; }; auto callbackColumnFunc = [&data](int32_t column) -> bool { data += ToCString(column + 1); return true; }; panda_file::File::EntityId methodId = method->GetFileId(); uint32_t offset = frameHandler.GetBytecodeOffset(); if (!debugExtractor->MatchLineWithOffset(callbackLineFunc, methodId, offset) || !debugExtractor->MatchColumnWithOffset(callbackColumnFunc, methodId, offset)) { data.push_back('?'); } data.push_back(')'); data.push_back('\n'); } } return data; } std::string ErrorHelper::BuildNativeAndJsStackTrace(JSThread *thread) { std::string stack = BuildNativeEcmaStackTrace(thread); return stack; } } // namespace panda::ecmascript::base