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