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/base/error_type.h"
19 #include "ecmascript/ecma_macros.h"
20 #include "ecmascript/ecma_vm.h"
21 #include "ecmascript/global_env.h"
22 #include "ecmascript/interpreter/frame_handler.h"
23 #include "ecmascript/interpreter/interpreter.h"
24 #include "ecmascript/js_object-inl.h"
25 #include "ecmascript/js_tagged_value-inl.h"
26 #include "ecmascript/jspandafile/js_pandafile_manager.h"
27 #include "ecmascript/object_factory.h"
28 #include "ecmascript/tooling/backend/js_pt_extractor.h"
29
30 namespace panda::ecmascript::base {
31 using panda::ecmascript::tooling::JSPtExtractor;
32
ErrorCommonToString(EcmaRuntimeCallInfo * argv,const ErrorType & errorType)33 JSTaggedValue ErrorHelper::ErrorCommonToString(EcmaRuntimeCallInfo *argv, const ErrorType &errorType)
34 {
35 ASSERT(argv);
36 JSThread *thread = argv->GetThread();
37 [[maybe_unused]] EcmaHandleScope handleScope(thread);
38
39 // 1. Let O be the this value.
40 // 2. If Type(O) is not Object, throw a TypeError exception
41 JSHandle<JSTaggedValue> thisValue = BuiltinsBase::GetThis(argv);
42 if (!thisValue->IsECMAObject()) {
43 THROW_TYPE_ERROR_AND_RETURN(thread, "ErrorToString:not an object", JSTaggedValue::Exception());
44 }
45 // 3. Let name be Get(O, "name").
46 // 4. ReturnIfAbrupt(name).
47 auto globalConst = thread->GlobalConstants();
48 JSHandle<JSTaggedValue> handleName = globalConst->GetHandledNameString();
49 JSHandle<JSTaggedValue> name = JSObject::GetProperty(thread, thisValue, handleName).GetValue();
50 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
51
52 // 5. If name is undefined, let name be "Error"; otherwise let name be ToString(name).
53 // 6. ReturnIfAbrupt(name).
54 name = ErrorHelper::GetErrorName(thread, name, errorType);
55 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
56
57 // 7. Let msg be Get(O, "message").
58 // 8. ReturnIfAbrupt(msg).
59 JSHandle<JSTaggedValue> handleMsg = globalConst->GetHandledMessageString();
60 JSHandle<JSTaggedValue> msg = JSObject::GetProperty(thread, thisValue, handleMsg).GetValue();
61 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
62
63 // 9. If msg is undefined, let msg be the empty String; otherwise let msg be ToString(msg).
64 // 10. ReturnIfAbrupt(msg).
65 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
66 if (msg->IsUndefined()) {
67 msg = JSHandle<JSTaggedValue>::Cast(factory->GetEmptyString());
68 } else {
69 msg = JSHandle<JSTaggedValue>::Cast(JSTaggedValue::ToString(thread, msg));
70 }
71 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
72
73 // 11. If name is the empty String, return msg.
74 // 12. If msg is the empty String, return name.
75 if (JSHandle<EcmaString>::Cast(name)->GetLength() == 0) {
76 return msg.GetTaggedValue();
77 }
78
79 if (JSHandle<EcmaString>::Cast(msg)->GetLength() == 0) {
80 return name.GetTaggedValue();
81 }
82
83 // 13. Return the result of concatenating name, the code unit 0x003A (COLON), the code unit 0x0020 (SPACE), and msg.
84 JSHandle<EcmaString> space = factory->NewFromCanBeCompressString(": ");
85 JSHandle<EcmaString> jsHandleName = JSHandle<EcmaString>::Cast(name);
86 JSHandle<EcmaString> jsHandleMsg = JSHandle<EcmaString>::Cast(msg);
87 JSHandle<EcmaString> handleNameSpace = factory->ConcatFromString(jsHandleName, space);
88 JSHandle<EcmaString> result = factory->ConcatFromString(handleNameSpace, jsHandleMsg);
89 return result.GetTaggedValue();
90 }
91
GetErrorName(JSThread * thread,const JSHandle<JSTaggedValue> & name,const ErrorType & errorType)92 JSHandle<JSTaggedValue> ErrorHelper::GetErrorName(JSThread *thread, const JSHandle<JSTaggedValue> &name,
93 const ErrorType &errorType)
94 {
95 auto globalConst = thread->GlobalConstants();
96 if (name->IsUndefined()) {
97 TaggedObject *errorKey = nullptr;
98 switch (errorType) {
99 case ErrorType::RANGE_ERROR:
100 errorKey = reinterpret_cast<TaggedObject *>(*globalConst->GetHandledRangeErrorString());
101 break;
102 case ErrorType::EVAL_ERROR:
103 errorKey = reinterpret_cast<TaggedObject *>(*globalConst->GetHandledEvalErrorString());
104 break;
105 case ErrorType::REFERENCE_ERROR:
106 errorKey = reinterpret_cast<TaggedObject *>(*globalConst->GetHandledReferenceErrorString());
107 break;
108 case ErrorType::TYPE_ERROR:
109 errorKey = reinterpret_cast<TaggedObject *>(*globalConst->GetHandledTypeErrorString());
110 break;
111 case ErrorType::URI_ERROR:
112 errorKey = reinterpret_cast<TaggedObject *>(*globalConst->GetHandledURIErrorString());
113 break;
114 case ErrorType::SYNTAX_ERROR:
115 errorKey = reinterpret_cast<TaggedObject *>(*globalConst->GetHandledSyntaxErrorString());
116 break;
117 default:
118 errorKey = reinterpret_cast<TaggedObject *>(*globalConst->GetHandledErrorString());
119 break;
120 }
121 return JSHandle<JSTaggedValue>(thread, JSTaggedValue(errorKey));
122 }
123 return JSHandle<JSTaggedValue>::Cast(JSTaggedValue::ToString(thread, name));
124 }
125
ErrorCommonConstructor(EcmaRuntimeCallInfo * argv,const ErrorType & errorType)126 JSTaggedValue ErrorHelper::ErrorCommonConstructor(EcmaRuntimeCallInfo *argv,
127 [[maybe_unused]] const ErrorType &errorType)
128 {
129 JSThread *thread = argv->GetThread();
130 [[maybe_unused]] EcmaHandleScope handleScope(thread);
131
132 // 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget.
133 auto ecmaVm = thread->GetEcmaVM();
134 ObjectFactory *factory = ecmaVm->GetFactory();
135 JSHandle<JSTaggedValue> ctor = BuiltinsBase::GetConstructor(argv);
136 JSMutableHandle<JSTaggedValue> newTarget(BuiltinsBase::GetNewTarget(argv));
137 if (newTarget->IsUndefined()) {
138 newTarget.Update(ctor.GetTaggedValue());
139 }
140 JSHandle<JSTaggedValue> message = BuiltinsBase::GetCallArg(argv, 0);
141
142 // 2. Let O be OrdinaryCreateFromConstructor(newTarget, "%ErrorPrototype%", «[[ErrorData]]»).
143 JSHandle<JSObject> nativeInstanceObj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(ctor), newTarget);
144
145 // 3. ReturnIfAbrupt(O).
146 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
147
148 // 4. If message is not undefined, then
149 // a. Let msg be ToString(message).
150 // b. ReturnIfAbrupt(msg).
151 // c. Let msgDesc be the PropertyDescriptor{[[Value]]: msg, [[Writable]]: true, [[Enumerable]]: false,
152 // [[Configurable]]: true}.
153 // d. Let status be DefinePropertyOrThrow(O, "message", msgDesc).
154 // e. Assert: status is not an abrupt completion
155 auto globalConst = thread->GlobalConstants();
156 if (!message->IsUndefined()) {
157 JSHandle<EcmaString> handleStr = JSTaggedValue::ToString(thread, message);
158 LOG(DEBUG, ECMASCRIPT) << "Ark throw error: " << utf::Mutf8AsCString(handleStr->GetDataUtf8());
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
166 JSHandle<EcmaString> handleStack = BuildEcmaStackTrace(thread);
167 JSHandle<JSTaggedValue> stackkey = globalConst->GetHandledStackString();
168 PropertyDescriptor stackDesc(thread, JSHandle<JSTaggedValue>::Cast(handleStack), true, false, true);
169 [[maybe_unused]] bool status = JSObject::DefineOwnProperty(thread, nativeInstanceObj, stackkey, stackDesc);
170 ASSERT_PRINT(status == true, "return result exception!");
171
172 // 5. Return O.
173 return nativeInstanceObj.GetTaggedValue();
174 }
175
DecodeFunctionName(const std::string & name)176 std::string ErrorHelper::DecodeFunctionName(const std::string &name)
177 {
178 if (name.empty()) {
179 return "anonymous";
180 }
181 return name;
182 }
183
BuildEcmaStackTrace(JSThread * thread)184 JSHandle<EcmaString> ErrorHelper::BuildEcmaStackTrace(JSThread *thread)
185 {
186 std::string data = BuildNativeEcmaStackTrace(thread);
187 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
188 LOG(DEBUG, ECMASCRIPT) << data;
189 return factory->NewFromStdString(data);
190 }
191
BuildNativeEcmaStackTrace(JSThread * thread)192 std::string ErrorHelper::BuildNativeEcmaStackTrace(JSThread *thread)
193 {
194 std::string data;
195 auto sp = const_cast<JSTaggedType *>(thread->GetCurrentSPFrame());
196 InterpretedFrameHandler frameHandler(sp);
197 for (; frameHandler.HasFrame(); frameHandler.PrevInterpretedFrame()) {
198 if (frameHandler.IsBreakFrame()) {
199 continue;
200 }
201 auto method = frameHandler.GetMethod();
202 if (! method->IsNative()) {
203 data.append(" at ");
204 data += DecodeFunctionName(method->ParseFunctionName());
205 data.append(" (");
206 // source file
207 tooling::JSPtExtractor *debugExtractor =
208 JSPandaFileManager::GetInstance()->GetJSPtExtractor(method->GetJSPandaFile());
209 const std::string &sourceFile = debugExtractor->GetSourceFile(method->GetFileId());
210 if (sourceFile.empty()) {
211 data.push_back('?');
212 } else {
213 data += sourceFile;
214 }
215 data.push_back(':');
216 // line number and column number
217 auto callbackLineFunc = [&data](int32_t line) -> bool {
218 data += ToCString(line + 1);
219 data.push_back(':');
220 return true;
221 };
222 auto callbackColumnFunc = [&data](int32_t column) -> bool {
223 data += ToCString(column + 1);
224 return true;
225 };
226 panda_file::File::EntityId methodId = method->GetFileId();
227 uint32_t offset = frameHandler.GetBytecodeOffset();
228 if (!debugExtractor->MatchLineWithOffset(callbackLineFunc, methodId, offset) ||
229 !debugExtractor->MatchColumnWithOffset(callbackColumnFunc, methodId, offset)) {
230 data.push_back('?');
231 }
232 data.push_back(')');
233 data.push_back('\n');
234 }
235 }
236
237 return data;
238 }
239
BuildNativeAndJsStackTrace(JSThread * thread)240 std::string ErrorHelper::BuildNativeAndJsStackTrace(JSThread *thread)
241 {
242 std::string stack = BuildNativeEcmaStackTrace(thread);
243 return stack;
244 }
245 } // namespace panda::ecmascript::base
246