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
21 namespace panda::ecmascript::base {
ErrorCommonToString(EcmaRuntimeCallInfo * argv,const ErrorType & errorType)22 JSTaggedValue 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
GetErrorName(JSThread * thread,const JSHandle<JSTaggedValue> & name,const ErrorType & errorType)80 JSHandle<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
ErrorCommonConstructor(EcmaRuntimeCallInfo * argv,const ErrorType & errorType)123 JSTaggedValue 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
GetErrorJSFunction(JSThread * thread)209 JSHandle<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
BuildEcmaStackTrace(JSThread * thread,std::string & stack,const JSHandle<JSObject> & jsErrorObj)228 JSHandle<EcmaString> ErrorHelper::BuildEcmaStackTrace(JSThread *thread, std::string &stack,
229 const JSHandle<JSObject> &jsErrorObj)
230 {
231 std::string data = JsStackInfo::BuildJsStackTrace(thread, false, jsErrorObj, true);
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