/* * 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/base/number_helper.h" #include "ecmascript/dfx/stackinfo/js_stackinfo.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" namespace panda::ecmascript::base { 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 (EcmaStringAccessor(JSHandle::Cast(name)).GetLength() == 0) { return msg.GetTaggedValue(); } if (EcmaStringAccessor(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->NewFromASCII(": "); 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::AGGREGATE_ERROR: errorKey = reinterpret_cast(*globalConst->GetHandledAggregateErrorString()); break; case ErrorType::URI_ERROR: errorKey = reinterpret_cast(*globalConst->GetHandledURIErrorString()); break; case ErrorType::SYNTAX_ERROR: errorKey = reinterpret_cast(*globalConst->GetHandledSyntaxErrorString()); break; case ErrorType::OOM_ERROR: errorKey = reinterpret_cast(*globalConst->GetHandledOOMErrorString()); 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); JSHandle newTarget = BuiltinsBase::GetNewTarget(argv); if (newTarget->IsUndefined()) { newTarget = ctor; } 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_ECMA(DEBUG) << "Throw error: " << EcmaStringAccessor(handleStr).ToCString(); 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 errorFunc = GetErrorJSFunction(thread); if (!errorFunc->IsUndefined()) { JSHandle errorFunckey = globalConst->GetHandledErrorFuncString(); PropertyDescriptor errorFuncDesc(thread, errorFunc, true, false, true); [[maybe_unused]] bool status = JSObject::DefineOwnProperty(thread, nativeInstanceObj, errorFunckey, errorFuncDesc); 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::GetErrorJSFunction(JSThread *thread) { FrameHandler frameHandler(thread); for (; frameHandler.HasFrame(); frameHandler.PrevJSFrame()) { if (!frameHandler.IsJSFrame()) { continue; } auto function = frameHandler.GetFunction(); if (function.IsJSFunctionBase() || function.IsJSProxy()) { Method *method = ECMAObject::Cast(function.GetTaggedObject())->GetCallTarget(); if (!method->IsNativeWithCallField()) { return JSHandle(thread, function); } } } return thread->GlobalConstants()->GetHandledUndefined(); } JSHandle ErrorHelper::BuildEcmaStackTrace(JSThread *thread) { std::string data = JsStackInfo::BuildJsStackTrace(thread, false); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); LOG_ECMA(DEBUG) << data; return factory->NewFromStdString(data); } } // namespace panda::ecmascript::base