• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "ecmascript/platform/log.h"
21 
22 namespace panda::ecmascript::base {
ErrorCommonToString(EcmaRuntimeCallInfo * argv,const ErrorType & errorType)23 JSTaggedValue ErrorHelper::ErrorCommonToString(EcmaRuntimeCallInfo *argv, const ErrorType &errorType)
24 {
25     ASSERT(argv);
26     JSThread *thread = argv->GetThread();
27     [[maybe_unused]] EcmaHandleScope handleScope(thread);
28 
29     // 1. Let O be the this value.
30     // 2. If Type(O) is not Object, throw a TypeError exception
31     JSHandle<JSTaggedValue> thisValue = BuiltinsBase::GetThis(argv);
32     if (!thisValue->IsECMAObject()) {
33         THROW_TYPE_ERROR_AND_RETURN(thread, "ErrorToString:not an object", JSTaggedValue::Exception());
34     }
35     // 3. Let name be Get(O, "name").
36     // 4. ReturnIfAbrupt(name).
37     auto globalConst = thread->GlobalConstants();
38     JSHandle<JSTaggedValue> handleName = globalConst->GetHandledNameString();
39     JSHandle<JSTaggedValue> name = JSObject::GetProperty(thread, thisValue, handleName).GetValue();
40     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
41 
42     // 5. If name is undefined, let name be "Error"; otherwise let name be ToString(name).
43     // 6. ReturnIfAbrupt(name).
44     name = ErrorHelper::GetErrorName(thread, name, errorType);
45     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
46 
47     // 7. Let msg be Get(O, "message").
48     // 8. ReturnIfAbrupt(msg).
49     JSHandle<JSTaggedValue> handleMsg = globalConst->GetHandledMessageString();
50     JSHandle<JSTaggedValue> msg = JSObject::GetProperty(thread, thisValue, handleMsg).GetValue();
51     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
52 
53     // 9. If msg is undefined, let msg be the empty String; otherwise let msg be ToString(msg).
54     // 10. ReturnIfAbrupt(msg).
55     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
56     if (msg->IsUndefined()) {
57         msg = JSHandle<JSTaggedValue>::Cast(factory->GetEmptyString());
58     } else {
59         msg = JSHandle<JSTaggedValue>::Cast(JSTaggedValue::ToString(thread, msg));
60     }
61     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
62 
63     // 11. If name is the empty String, return msg.
64     // 12. If msg is the empty String, return name.
65     if (EcmaStringAccessor(JSHandle<EcmaString>::Cast(name)).GetLength() == 0) {
66         return msg.GetTaggedValue();
67     }
68     if (EcmaStringAccessor(JSHandle<EcmaString>::Cast(msg)).GetLength() == 0) {
69         return name.GetTaggedValue();
70     }
71 
72     // 13. Return the result of concatenating name, the code unit 0x003A (COLON), the code unit 0x0020 (SPACE), and msg.
73     JSHandle<EcmaString> space = factory->NewFromASCII(": ");
74     JSHandle<EcmaString> jsHandleName = JSHandle<EcmaString>::Cast(name);
75     JSHandle<EcmaString> jsHandleMsg = JSHandle<EcmaString>::Cast(msg);
76     JSHandle<EcmaString> handleNameSpace = factory->ConcatFromString(jsHandleName, space);
77     JSHandle<EcmaString> result = factory->ConcatFromString(handleNameSpace, jsHandleMsg);
78     return result.GetTaggedValue();
79 }
80 
GetErrorName(JSThread * thread,const JSHandle<JSTaggedValue> & name,const ErrorType & errorType)81 JSHandle<JSTaggedValue> ErrorHelper::GetErrorName(JSThread *thread, const JSHandle<JSTaggedValue> &name,
82                                                   const ErrorType &errorType)
83 {
84     auto globalConst = thread->GlobalConstants();
85     if (name->IsUndefined()) {
86         JSHandle<JSTaggedValue> errorKey;
87         switch (errorType) {
88             case ErrorType::RANGE_ERROR:
89                 errorKey = globalConst->GetHandledRangeErrorString();
90                 break;
91             case ErrorType::EVAL_ERROR:
92                 errorKey = globalConst->GetHandledEvalErrorString();
93                 break;
94             case ErrorType::REFERENCE_ERROR:
95                 errorKey = globalConst->GetHandledReferenceErrorString();
96                 break;
97             case ErrorType::TYPE_ERROR:
98                 errorKey = globalConst->GetHandledTypeErrorString();
99                 break;
100             case ErrorType::AGGREGATE_ERROR:
101                 errorKey = globalConst->GetHandledAggregateErrorString();
102                 break;
103             case ErrorType::URI_ERROR:
104                 errorKey = globalConst->GetHandledURIErrorString();
105                 break;
106             case ErrorType::SYNTAX_ERROR:
107                 errorKey = globalConst->GetHandledSyntaxErrorString();
108                 break;
109             case ErrorType::OOM_ERROR:
110                 errorKey = globalConst->GetHandledOOMErrorString();
111                 break;
112             case ErrorType::TERMINATION_ERROR:
113                 errorKey = globalConst->GetHandledTerminationErrorString();
114                 break;
115             default:
116                 errorKey = globalConst->GetHandledErrorString();
117                 break;
118         }
119         return errorKey;
120     }
121     return JSHandle<JSTaggedValue>::Cast(JSTaggedValue::ToString(thread, name));
122 }
123 
ErrorCommonConstructor(EcmaRuntimeCallInfo * argv,const ErrorType & errorType)124 JSTaggedValue ErrorHelper::ErrorCommonConstructor(EcmaRuntimeCallInfo *argv,
125                                                   [[maybe_unused]] const ErrorType &errorType)
126 {
127     JSThread *thread = argv->GetThread();
128     [[maybe_unused]] EcmaHandleScope handleScope(thread);
129 
130     // 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget.
131     auto ecmaVm = thread->GetEcmaVM();
132     ObjectFactory *factory = ecmaVm->GetFactory();
133     JSHandle<JSTaggedValue> ctor = BuiltinsBase::GetConstructor(argv);
134     JSHandle<JSTaggedValue> newTarget = BuiltinsBase::GetNewTarget(argv);
135     if (newTarget->IsUndefined()) {
136         newTarget = ctor;
137     }
138     JSHandle<JSTaggedValue> message = BuiltinsBase::GetCallArg(argv, 0);
139 
140     // 2. Let O be OrdinaryCreateFromConstructor(newTarget, "%ErrorPrototype%", «[[ErrorData]]»).
141     JSHandle<JSObject> nativeInstanceObj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(ctor), newTarget);
142 
143     // 3. ReturnIfAbrupt(O).
144     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
145 
146     // 4. If message is not undefined, then
147     //    a. Let msg be ToString(message).
148     //    b. ReturnIfAbrupt(msg).
149     //    c. Let msgDesc be the PropertyDescriptor{[[Value]]: msg, [[Writable]]: true, [[Enumerable]]: false,
150     //       [[Configurable]]: true}.
151     //    d. Let status be DefinePropertyOrThrow(O, "message", msgDesc).
152     //    e. Assert: status is not an abrupt completion
153     auto globalConst = thread->GlobalConstants();
154     if (!message->IsUndefined()) {
155         JSHandle<EcmaString> handleStr = JSTaggedValue::ToString(thread, message);
156         if (errorType != ErrorType::OOM_ERROR) {
157             LOG_ECMA(DEBUG) << "Throw error: " << EcmaStringAccessor(handleStr).ToCString(thread);
158         }
159         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
160         JSHandle<JSTaggedValue> msgKey = globalConst->GetHandledMessageString();
161         PropertyDescriptor msgDesc(thread, JSHandle<JSTaggedValue>::Cast(handleStr), true, false, true);
162         [[maybe_unused]] bool status = JSObject::DefineOwnProperty(thread, nativeInstanceObj, msgKey, msgDesc);
163         ASSERT_PRINT(status == true, "return result exception!");
164     }
165     // InstallErrorCause
166     JSHandle<JSTaggedValue> options = BuiltinsBase::GetCallArg(argv, 1);
167     // If options is an Object and ? HasProperty(options, "cause") is true, then
168     //   a. Let cause be ? Get(options, "cause").
169     //   b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "cause", cause).
170     if (options->IsECMAObject()) {
171         JSHandle<JSTaggedValue> causeKey = globalConst->GetHandledCauseString();
172         bool causePresent = JSTaggedValue::HasProperty(thread, options, causeKey);
173         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
174         if (causePresent) {
175             JSHandle<JSTaggedValue> cause = JSObject::GetProperty(thread, options, causeKey).GetValue();
176             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
177             PropertyDescriptor causeDesc(thread, cause, true, false, true);
178             [[maybe_unused]] bool status = JSObject::DefineOwnProperty(thread, nativeInstanceObj, causeKey, causeDesc);
179             ASSERT_PRINT(status == true, "return result exception!");
180         }
181     }
182     JSHandle<JSTaggedValue> errorFunc = GetErrorJSFunction(thread);
183     if (!errorFunc->IsUndefined()) {
184         JSHandle<JSTaggedValue> errorFunckey = globalConst->GetHandledErrorFuncString();
185         PropertyDescriptor errorFuncDesc(thread, errorFunc, true, false, true);
186         [[maybe_unused]] bool status = JSObject::DefineOwnProperty(thread,
187                                                                    nativeInstanceObj, errorFunckey, errorFuncDesc);
188         ASSERT_PRINT(status == true, "return result exception!");
189     }
190 
191     std::string stackTrace = BuildStackTraceWithLimit(thread, nativeInstanceObj);
192     JSHandle<EcmaString> stackTraceStr = factory->NewFromStdString(stackTrace);
193     JSHandle<EcmaString> cbStackTraceStr;
194     auto sourceMapcb = ecmaVm->GetSourceMapCallback();
195     if (sourceMapcb != nullptr && !stackTrace.empty()) {
196         cbStackTraceStr = factory->NewFromStdString(sourceMapcb(stackTrace.c_str()));
197     } else {
198         cbStackTraceStr = stackTraceStr;
199     }
200 
201     PropertyDescriptor stackDesc(thread, JSHandle<JSTaggedValue>::Cast(cbStackTraceStr), true, false, true);
202     [[maybe_unused]] bool status = JSObject::DefineOwnProperty(thread, nativeInstanceObj,
203         globalConst->GetHandledStackString(), stackDesc);
204     ASSERT_PRINT(status == true, "return result exception!");
205 
206     // Uncaught exception parsing source code
207     PropertyDescriptor topStackDesc(thread, JSHandle<JSTaggedValue>::Cast(stackTraceStr), true, false, true);
208     [[maybe_unused]] bool topStackstatus = JSObject::DefineOwnProperty(thread, nativeInstanceObj,
209         globalConst->GetHandledTopStackString(), topStackDesc);
210     ASSERT_PRINT(topStackstatus == true, "return result exception!");
211 
212     // 5. Return O.
213     return nativeInstanceObj.GetTaggedValue();
214 }
215 
216 // static
PrintJSErrorInfo(JSThread * thread,const JSHandle<JSTaggedValue> exceptionInfo)217 void ErrorHelper::PrintJSErrorInfo(JSThread *thread, const JSHandle<JSTaggedValue> exceptionInfo)
218 {
219     CString nameBuffer = GetJSErrorInfo(thread, exceptionInfo, JSErrorProps::NAME);
220     CString msgBuffer = GetJSErrorInfo(thread, exceptionInfo, JSErrorProps::MESSAGE);
221     CString stackBuffer = GetJSErrorInfo(thread, exceptionInfo, JSErrorProps::STACK);
222     LOG_NO_TAG(ERROR) << panda::ecmascript::previewerTag << nameBuffer << ": " << msgBuffer << "\n"
223                       << (panda::ecmascript::previewerTag.empty()
224                               ? stackBuffer
225                               : std::regex_replace(stackBuffer, std::regex(".+(\n|$)"),
226                                                    panda::ecmascript::previewerTag + "$0"));
227 }
228 
229 // static
GetJSErrorInfo(JSThread * thread,const JSHandle<JSTaggedValue> exceptionInfo,JSErrorProps key)230 CString ErrorHelper::GetJSErrorInfo(JSThread *thread, const JSHandle<JSTaggedValue> exceptionInfo, JSErrorProps key)
231 {
232     JSHandle<JSTaggedValue> keyStr(thread, JSTaggedValue::Undefined());
233     switch (key) {
234         case JSErrorProps::NAME:
235             keyStr = thread->GlobalConstants()->GetHandledNameString();
236             break;
237         case JSErrorProps::MESSAGE:
238             keyStr = thread->GlobalConstants()->GetHandledMessageString();
239             break;
240         case JSErrorProps::STACK:
241             keyStr = thread->GlobalConstants()->GetHandledStackString();
242             break;
243         default:
244             LOG_ECMA(FATAL) << "this branch is unreachable " << key;
245             UNREACHABLE();
246     }
247     JSHandle<JSTaggedValue> value = JSObject::GetProperty(thread, exceptionInfo, keyStr).GetValue();
248     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, CString());
249     JSHandle<EcmaString> errStr = JSTaggedValue::ToString(thread, value);
250     // JSTaggedValue::ToString may cause exception. In this case, do not return, use "<error>" instead.
251     if (thread->HasPendingException()) {
252         thread->ClearException();
253         errStr = thread->GetEcmaVM()->GetFactory()->NewFromStdString("<error>");
254     }
255     return ConvertToString(thread, *errStr);
256 }
257 
GetErrorJSFunction(JSThread * thread)258 JSHandle<JSTaggedValue> ErrorHelper::GetErrorJSFunction(JSThread *thread)
259 {
260     FrameHandler frameHandler(thread);
261     for (; frameHandler.HasFrame(); frameHandler.PrevJSFrame()) {
262         if (!frameHandler.IsJSFrame()) {
263             continue;
264         }
265 
266         auto function = frameHandler.GetFunction();
267         if (function.IsJSFunctionBase() || function.IsJSProxy()) {
268             Method *method = ECMAObject::Cast(function.GetTaggedObject())->GetCallTarget(thread);
269             if (!method->IsNativeWithCallField()) {
270                 return JSHandle<JSTaggedValue>(thread, function);
271             }
272         }
273     }
274     return thread->GlobalConstants()->GetHandledUndefined();
275 }
276 
BuildStackTraceWithLimit(JSThread * thread,const JSHandle<JSObject> & jsErrorObj)277 std::string ErrorHelper::BuildStackTraceWithLimit(JSThread *thread, const JSHandle<JSObject> &jsErrorObj)
278 {
279     std::string data = JsStackInfo::BuildJsStackTrace(thread, false, jsErrorObj, true);
280     if (data.size() > MAX_ERROR_SIZE) {
281         // find last line break from 0 to MAX_ERROR_SIZE
282         size_t pos = data.rfind('\n', MAX_ERROR_SIZE);
283         if (pos != std::string::npos) {
284             data.resize(pos);
285         }
286     }
287     LOG_ECMA(DEBUG) << data;
288     return data;
289 }
290 }  // namespace panda::ecmascript::base
291