1/* 2 * Copyright (c) 2021 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16#include "ecmascript/base/error_helper.h" 17#include "ecmascript/base/builtins_base.h" 18#include "ecmascript/dfx/stackinfo/js_stackinfo.h" 19#include "ecmascript/interpreter/frame_handler.h" 20 21namespace panda::ecmascript::base { 22JSTaggedValue ErrorHelper::ErrorCommonToString(EcmaRuntimeCallInfo *argv, const ErrorType &errorType) 23{ 24 ASSERT(argv); 25 JSThread *thread = argv->GetThread(); 26 [[maybe_unused]] EcmaHandleScope handleScope(thread); 27 28 // 1. Let O be the this value. 29 // 2. If Type(O) is not Object, throw a TypeError exception 30 JSHandle<JSTaggedValue> thisValue = BuiltinsBase::GetThis(argv); 31 if (!thisValue->IsECMAObject()) { 32 THROW_TYPE_ERROR_AND_RETURN(thread, "ErrorToString:not an object", JSTaggedValue::Exception()); 33 } 34 // 3. Let name be Get(O, "name"). 35 // 4. ReturnIfAbrupt(name). 36 auto globalConst = thread->GlobalConstants(); 37 JSHandle<JSTaggedValue> handleName = globalConst->GetHandledNameString(); 38 JSHandle<JSTaggedValue> name = JSObject::GetProperty(thread, thisValue, handleName).GetValue(); 39 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 40 41 // 5. If name is undefined, let name be "Error"; otherwise let name be ToString(name). 42 // 6. ReturnIfAbrupt(name). 43 name = ErrorHelper::GetErrorName(thread, name, errorType); 44 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 45 46 // 7. Let msg be Get(O, "message"). 47 // 8. ReturnIfAbrupt(msg). 48 JSHandle<JSTaggedValue> handleMsg = globalConst->GetHandledMessageString(); 49 JSHandle<JSTaggedValue> msg = JSObject::GetProperty(thread, thisValue, handleMsg).GetValue(); 50 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 51 52 // 9. If msg is undefined, let msg be the empty String; otherwise let msg be ToString(msg). 53 // 10. ReturnIfAbrupt(msg). 54 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 55 if (msg->IsUndefined()) { 56 msg = JSHandle<JSTaggedValue>::Cast(factory->GetEmptyString()); 57 } else { 58 msg = JSHandle<JSTaggedValue>::Cast(JSTaggedValue::ToString(thread, msg)); 59 } 60 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 61 62 // 11. If name is the empty String, return msg. 63 // 12. If msg is the empty String, return name. 64 if (EcmaStringAccessor(JSHandle<EcmaString>::Cast(name)).GetLength() == 0) { 65 return msg.GetTaggedValue(); 66 } 67 if (EcmaStringAccessor(JSHandle<EcmaString>::Cast(msg)).GetLength() == 0) { 68 return name.GetTaggedValue(); 69 } 70 71 // 13. Return the result of concatenating name, the code unit 0x003A (COLON), the code unit 0x0020 (SPACE), and msg. 72 JSHandle<EcmaString> space = factory->NewFromASCII(": "); 73 JSHandle<EcmaString> jsHandleName = JSHandle<EcmaString>::Cast(name); 74 JSHandle<EcmaString> jsHandleMsg = JSHandle<EcmaString>::Cast(msg); 75 JSHandle<EcmaString> handleNameSpace = factory->ConcatFromString(jsHandleName, space); 76 JSHandle<EcmaString> result = factory->ConcatFromString(handleNameSpace, jsHandleMsg); 77 return result.GetTaggedValue(); 78} 79 80JSHandle<JSTaggedValue> ErrorHelper::GetErrorName(JSThread *thread, const JSHandle<JSTaggedValue> &name, 81 const ErrorType &errorType) 82{ 83 auto globalConst = thread->GlobalConstants(); 84 if (name->IsUndefined()) { 85 JSHandle<JSTaggedValue> errorKey; 86 switch (errorType) { 87 case ErrorType::RANGE_ERROR: 88 errorKey = globalConst->GetHandledRangeErrorString(); 89 break; 90 case ErrorType::EVAL_ERROR: 91 errorKey = globalConst->GetHandledEvalErrorString(); 92 break; 93 case ErrorType::REFERENCE_ERROR: 94 errorKey = globalConst->GetHandledReferenceErrorString(); 95 break; 96 case ErrorType::TYPE_ERROR: 97 errorKey = globalConst->GetHandledTypeErrorString(); 98 break; 99 case ErrorType::AGGREGATE_ERROR: 100 errorKey = globalConst->GetHandledAggregateErrorString(); 101 break; 102 case ErrorType::URI_ERROR: 103 errorKey = globalConst->GetHandledURIErrorString(); 104 break; 105 case ErrorType::SYNTAX_ERROR: 106 errorKey = globalConst->GetHandledSyntaxErrorString(); 107 break; 108 case ErrorType::OOM_ERROR: 109 errorKey = globalConst->GetHandledOOMErrorString(); 110 break; 111 case ErrorType::TERMINATION_ERROR: 112 errorKey = globalConst->GetHandledTerminationErrorString(); 113 break; 114 default: 115 errorKey = globalConst->GetHandledErrorString(); 116 break; 117 } 118 return errorKey; 119 } 120 return JSHandle<JSTaggedValue>::Cast(JSTaggedValue::ToString(thread, name)); 121} 122 123JSTaggedValue ErrorHelper::ErrorCommonConstructor(EcmaRuntimeCallInfo *argv, 124 [[maybe_unused]] const ErrorType &errorType) 125{ 126 JSThread *thread = argv->GetThread(); 127 [[maybe_unused]] EcmaHandleScope handleScope(thread); 128 129 // 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget. 130 auto ecmaVm = thread->GetEcmaVM(); 131 ObjectFactory *factory = ecmaVm->GetFactory(); 132 JSHandle<JSTaggedValue> ctor = BuiltinsBase::GetConstructor(argv); 133 JSHandle<JSTaggedValue> newTarget = BuiltinsBase::GetNewTarget(argv); 134 if (newTarget->IsUndefined()) { 135 newTarget = ctor; 136 } 137 JSHandle<JSTaggedValue> message = BuiltinsBase::GetCallArg(argv, 0); 138 139 // 2. Let O be OrdinaryCreateFromConstructor(newTarget, "%ErrorPrototype%", «[[ErrorData]]»). 140 JSHandle<JSObject> nativeInstanceObj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(ctor), newTarget); 141 142 // 3. ReturnIfAbrupt(O). 143 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 144 145 // 4. If message is not undefined, then 146 // a. Let msg be ToString(message). 147 // b. ReturnIfAbrupt(msg). 148 // c. Let msgDesc be the PropertyDescriptor{[[Value]]: msg, [[Writable]]: true, [[Enumerable]]: false, 149 // [[Configurable]]: true}. 150 // d. Let status be DefinePropertyOrThrow(O, "message", msgDesc). 151 // e. Assert: status is not an abrupt completion 152 auto globalConst = thread->GlobalConstants(); 153 if (!message->IsUndefined()) { 154 JSHandle<EcmaString> handleStr = JSTaggedValue::ToString(thread, message); 155 if (errorType != ErrorType::OOM_ERROR) { 156 LOG_ECMA(DEBUG) << "Throw error: " << EcmaStringAccessor(handleStr).ToCString(); 157 } 158 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 159 JSHandle<JSTaggedValue> msgKey = globalConst->GetHandledMessageString(); 160 PropertyDescriptor msgDesc(thread, JSHandle<JSTaggedValue>::Cast(handleStr), true, false, true); 161 [[maybe_unused]] bool status = JSObject::DefineOwnProperty(thread, nativeInstanceObj, msgKey, msgDesc); 162 ASSERT_PRINT(status == true, "return result exception!"); 163 } 164 // InstallErrorCause 165 JSHandle<JSTaggedValue> options = BuiltinsBase::GetCallArg(argv, 1); 166 // If options is an Object and ? HasProperty(options, "cause") is true, then 167 // a. Let cause be ? Get(options, "cause"). 168 // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "cause", cause). 169 if (options->IsECMAObject()) { 170 JSHandle<JSTaggedValue> causeKey = globalConst->GetHandledCauseString(); 171 bool causePresent = JSTaggedValue::HasProperty(thread, options, causeKey); 172 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 173 if (causePresent) { 174 JSHandle<JSTaggedValue> cause = JSObject::GetProperty(thread, options, causeKey).GetValue(); 175 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 176 PropertyDescriptor causeDesc(thread, cause, true, false, true); 177 [[maybe_unused]] bool status = JSObject::DefineOwnProperty(thread, nativeInstanceObj, causeKey, causeDesc); 178 ASSERT_PRINT(status == true, "return result exception!"); 179 } 180 } 181 JSHandle<JSTaggedValue> errorFunc = GetErrorJSFunction(thread); 182 if (!errorFunc->IsUndefined()) { 183 JSHandle<JSTaggedValue> errorFunckey = globalConst->GetHandledErrorFuncString(); 184 PropertyDescriptor errorFuncDesc(thread, errorFunc, true, false, true); 185 [[maybe_unused]] bool status = JSObject::DefineOwnProperty(thread, 186 nativeInstanceObj, errorFunckey, errorFuncDesc); 187 ASSERT_PRINT(status == true, "return result exception!"); 188 } 189 190 std::string stack; 191 JSHandle<EcmaString> handleStack = BuildEcmaStackTrace(thread, stack, nativeInstanceObj); 192 JSHandle<JSTaggedValue> stackkey = globalConst->GetHandledStackString(); 193 PropertyDescriptor stackDesc(thread, JSHandle<JSTaggedValue>::Cast(handleStack), true, false, true); 194 [[maybe_unused]] bool status = JSObject::DefineOwnProperty(thread, nativeInstanceObj, stackkey, stackDesc); 195 ASSERT_PRINT(status == true, "return result exception!"); 196 197 // Uncaught exception parsing source code 198 JSHandle<JSTaggedValue> topStackkey = globalConst->GetHandledTopStackString(); 199 PropertyDescriptor topStackDesc(thread, JSHandle<JSTaggedValue>::Cast(factory->NewFromStdString(stack)), 200 true, false, true); 201 [[maybe_unused]] bool topStackstatus = JSObject::DefineOwnProperty(thread, nativeInstanceObj, 202 topStackkey, topStackDesc); 203 ASSERT_PRINT(topStackstatus == true, "return result exception!"); 204 205 // 5. Return O. 206 return nativeInstanceObj.GetTaggedValue(); 207} 208 209JSHandle<JSTaggedValue> ErrorHelper::GetErrorJSFunction(JSThread *thread) 210{ 211 FrameHandler frameHandler(thread); 212 for (; frameHandler.HasFrame(); frameHandler.PrevJSFrame()) { 213 if (!frameHandler.IsJSFrame()) { 214 continue; 215 } 216 217 auto function = frameHandler.GetFunction(); 218 if (function.IsJSFunctionBase() || function.IsJSProxy()) { 219 Method *method = ECMAObject::Cast(function.GetTaggedObject())->GetCallTarget(); 220 if (!method->IsNativeWithCallField()) { 221 return JSHandle<JSTaggedValue>(thread, function); 222 } 223 } 224 } 225 return thread->GlobalConstants()->GetHandledUndefined(); 226} 227 228JSHandle<EcmaString> ErrorHelper::BuildEcmaStackTrace(JSThread *thread, std::string &stack, 229 const JSHandle<JSObject> &jsErrorObj) 230{ 231 std::string data = JsStackInfo::BuildJsStackTrace(thread, false, jsErrorObj); 232 if (data.size() > MAX_ERROR_SIZE) { 233 // find last line break from 0 to MAX_ERROR_SIZE 234 size_t pos = data.rfind('\n', MAX_ERROR_SIZE); 235 if (pos != std::string::npos) { 236 data = data.substr(0, pos); 237 } 238 } 239 LOG_ECMA(DEBUG) << data; 240 // unconverted stack 241 stack = data; 242 auto ecmaVm = thread->GetEcmaVM(); 243 // sourceMap callback 244 auto sourceMapcb = ecmaVm->GetSourceMapCallback(); 245 if (sourceMapcb != nullptr && !data.empty()) { 246 data = sourceMapcb(data.c_str()); 247 } 248 249 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 250 return factory->NewFromStdString(data); 251} 252} // namespace panda::ecmascript::base 253