• 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/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