• 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/tooling/agent/runtime_impl.h"
17 
18 #include <iomanip>
19 
20 #include "ecmascript/napi/include/dfx_jsnapi.h"
21 #include "ecmascript/tooling/base/pt_returns.h"
22 #include "ecmascript/tooling/protocol_channel.h"
23 #include "libpandabase/utils/logger.h"
24 
25 namespace panda::ecmascript::tooling {
Dispatch(const DispatchRequest & request)26 void RuntimeImpl::DispatcherImpl::Dispatch(const DispatchRequest &request)
27 {
28     static std::unordered_map<std::string, AgentHandler> dispatcherTable {
29         { "enable", &RuntimeImpl::DispatcherImpl::Enable },
30         { "getProperties", &RuntimeImpl::DispatcherImpl::GetProperties },
31         { "runIfWaitingForDebugger", &RuntimeImpl::DispatcherImpl::RunIfWaitingForDebugger },
32         { "callFunctionOn", &RuntimeImpl::DispatcherImpl::CallFunctionOn },
33         { "getHeapUsage", &RuntimeImpl::DispatcherImpl::GetHeapUsage }
34     };
35 
36     const std::string &method = request.GetMethod();
37     LOG(DEBUG, DEBUGGER) << "dispatch [" << method << "] to RuntimeImpl";
38 
39     auto entry = dispatcherTable.find(method);
40     if (entry != dispatcherTable.end()) {
41         (this->*(entry->second))(request);
42     } else {
43         LOG(ERROR, DEBUGGER) << "unknown method: " << method;
44         SendResponse(request, DispatchResponse::Fail("unknown method: " + method));
45     }
46 }
47 
Enable(const DispatchRequest & request)48 void RuntimeImpl::DispatcherImpl::Enable(const DispatchRequest &request)
49 {
50     DispatchResponse response = runtime_->Enable();
51     SendResponse(request, response);
52 }
53 
Disable(const DispatchRequest & request)54 void RuntimeImpl::DispatcherImpl::Disable(const DispatchRequest &request)
55 {
56     DispatchResponse response = runtime_->Disable();
57     SendResponse(request, response);
58 }
59 
RunIfWaitingForDebugger(const DispatchRequest & request)60 void RuntimeImpl::DispatcherImpl::RunIfWaitingForDebugger(const DispatchRequest &request)
61 {
62     DispatchResponse response = runtime_->RunIfWaitingForDebugger();
63     SendResponse(request, response);
64 }
65 
GetProperties(const DispatchRequest & request)66 void RuntimeImpl::DispatcherImpl::GetProperties(const DispatchRequest &request)
67 {
68     std::unique_ptr<GetPropertiesParams> params = GetPropertiesParams::Create(request.GetParams());
69     if (params == nullptr) {
70         SendResponse(request, DispatchResponse::Fail("wrong params"));
71         return;
72     }
73 
74     std::vector<std::unique_ptr<PropertyDescriptor>> outPropertyDesc;
75     std::optional<std::vector<std::unique_ptr<InternalPropertyDescriptor>>> outInternalDescs;
76     std::optional<std::vector<std::unique_ptr<PrivatePropertyDescriptor>>> outPrivateProperties;
77     std::optional<std::unique_ptr<ExceptionDetails>> outExceptionDetails;
78     DispatchResponse response = runtime_->GetProperties(*params, &outPropertyDesc, &outInternalDescs,
79         &outPrivateProperties, &outExceptionDetails);
80     if (outExceptionDetails) {
81         LOG(WARNING, DEBUGGER) << "GetProperties thrown an exception";
82     }
83     GetPropertiesReturns result(std::move(outPropertyDesc),
84         std::move(outInternalDescs),
85         std::move(outPrivateProperties),
86         std::move(outExceptionDetails));
87     SendResponse(request, response, result);
88 }
89 
CallFunctionOn(const DispatchRequest & request)90 void RuntimeImpl::DispatcherImpl::CallFunctionOn(const DispatchRequest &request)
91 {
92     std::unique_ptr<CallFunctionOnParams> params = CallFunctionOnParams::Create(request.GetParams());
93     if (params == nullptr) {
94         SendResponse(request, DispatchResponse::Fail("wrong params"));
95         return;
96     }
97 
98     std::unique_ptr<RemoteObject> outRemoteObject;
99     std::optional<std::unique_ptr<ExceptionDetails>> outExceptionDetails;
100     DispatchResponse response = runtime_->CallFunctionOn(*params, &outRemoteObject, &outExceptionDetails);
101     if (outExceptionDetails) {
102         LOG(WARNING, DEBUGGER) << "CallFunctionOn thrown an exception";
103     }
104     CallFunctionOnReturns result(std::move(outRemoteObject), std::move(outExceptionDetails));
105     SendResponse(request, response, result);
106 }
107 
GetHeapUsage(const DispatchRequest & request)108 void RuntimeImpl::DispatcherImpl::GetHeapUsage(const DispatchRequest &request)
109 {
110     double usedSize = 0;
111     double totalSize = 0;
112     DispatchResponse response = runtime_->GetHeapUsage(&usedSize, &totalSize);
113     GetHeapUsageReturns result(usedSize, totalSize);
114     SendResponse(request, response, result);
115 }
116 
AllowNotify() const117 bool RuntimeImpl::Frontend::AllowNotify() const
118 {
119     return channel_ != nullptr;
120 }
121 
RunIfWaitingForDebugger()122 void RuntimeImpl::Frontend::RunIfWaitingForDebugger()
123 {
124     if (!AllowNotify()) {
125         return;
126     }
127 
128     channel_->RunIfWaitingForDebugger();
129 }
130 
Enable()131 DispatchResponse RuntimeImpl::Enable()
132 {
133     return DispatchResponse::Ok();
134 }
135 
Disable()136 DispatchResponse RuntimeImpl::Disable()
137 {
138     return DispatchResponse::Ok();
139 }
140 
RunIfWaitingForDebugger()141 DispatchResponse RuntimeImpl::RunIfWaitingForDebugger()
142 {
143     frontend_.RunIfWaitingForDebugger();
144     return DispatchResponse::Ok();
145 }
146 
CallFunctionOn(const CallFunctionOnParams & params,std::unique_ptr<RemoteObject> * outRemoteObject,std::optional<std::unique_ptr<ExceptionDetails>> * outExceptionDetails)147 DispatchResponse RuntimeImpl::CallFunctionOn([[maybe_unused]] const CallFunctionOnParams &params,
148     std::unique_ptr<RemoteObject> *outRemoteObject,
149     [[maybe_unused]] std::optional<std::unique_ptr<ExceptionDetails>> *outExceptionDetails)
150 {
151     // Return EvalError temporarily.
152     auto error = Exception::EvalError(vm_, StringRef::NewFromUtf8(vm_, "Unsupport eval now"));
153     *outRemoteObject = RemoteObject::FromTagged(vm_, error);
154     return DispatchResponse::Ok();
155 }
156 
GetHeapUsage(double * usedSize,double * totalSize)157 DispatchResponse RuntimeImpl::GetHeapUsage(double *usedSize, double *totalSize)
158 {
159     *totalSize = static_cast<double>(DFXJSNApi::GetHeapTotalSize(vm_));
160     *usedSize = static_cast<double>(DFXJSNApi::GetHeapUsedSize(vm_));
161     return DispatchResponse::Ok();
162 }
163 
GetProperties(const GetPropertiesParams & params,std::vector<std::unique_ptr<PropertyDescriptor>> * outPropertyDesc,std::optional<std::vector<std::unique_ptr<InternalPropertyDescriptor>>> * outInternalDescs,std::optional<std::vector<std::unique_ptr<PrivatePropertyDescriptor>>> * outPrivateProps,std::optional<std::unique_ptr<ExceptionDetails>> * outExceptionDetails)164 DispatchResponse RuntimeImpl::GetProperties(const GetPropertiesParams &params,
165     std::vector<std::unique_ptr<PropertyDescriptor>> *outPropertyDesc,
166     [[maybe_unused]] std::optional<std::vector<std::unique_ptr<InternalPropertyDescriptor>>> *outInternalDescs,
167     [[maybe_unused]] std::optional<std::vector<std::unique_ptr<PrivatePropertyDescriptor>>> *outPrivateProps,
168     [[maybe_unused]] std::optional<std::unique_ptr<ExceptionDetails>> *outExceptionDetails)
169 {
170     RemoteObjectId objectId = params.GetObjectId();
171     bool isOwn = params.GetOwnProperties();
172     bool isAccessorOnly = params.GetAccessPropertiesOnly();
173     auto iter = properties_.find(objectId);
174     if (iter == properties_.end()) {
175         LOG(ERROR, DEBUGGER) << "RuntimeImpl::GetProperties Unknown object id: " << objectId;
176         return DispatchResponse::Fail("Unknown object id");
177     }
178     Local<JSValueRef> value = Local<JSValueRef>(vm_, iter->second);
179     if (value.IsEmpty() || !value->IsObject()) {
180         LOG(ERROR, DEBUGGER) << "RuntimeImpl::GetProperties should a js object";
181         return DispatchResponse::Fail("Not a object");
182     }
183     if (value->IsArrayBuffer()) {
184         Local<ArrayBufferRef> arrayBufferRef(value);
185         AddTypedArrayRefs(arrayBufferRef, outPropertyDesc);
186     }
187     Local<ArrayRef> keys = Local<ObjectRef>(value)->GetOwnPropertyNames(vm_);
188     int32_t length = keys->Length(vm_);
189     Local<JSValueRef> name = JSValueRef::Undefined(vm_);
190     for (int32_t i = 0; i < length; ++i) {
191         name = keys->Get(vm_, i);
192         PropertyAttribute jsProperty = PropertyAttribute::Default();
193         if (!Local<ObjectRef>(value)->GetOwnProperty(vm_, name, jsProperty)) {
194             continue;
195         }
196         std::unique_ptr<PropertyDescriptor> debuggerProperty =
197             PropertyDescriptor::FromProperty(vm_, name, jsProperty);
198         if (isAccessorOnly && !jsProperty.HasGetter() && !jsProperty.HasSetter()) {
199             continue;
200         }
201         if (debuggerProperty->HasGet()) {
202             debuggerProperty->GetGet()->SetObjectId(curObjectId_);
203             properties_[curObjectId_++] = Global<JSValueRef>(vm_, jsProperty.GetGetter(vm_));
204         }
205         if (debuggerProperty->HasSet()) {
206             debuggerProperty->GetSet()->SetObjectId(curObjectId_);
207             properties_[curObjectId_++] = Global<JSValueRef>(vm_, jsProperty.GetSetter(vm_));
208         }
209         if (debuggerProperty->HasValue()) {
210             Local<JSValueRef> vValue = jsProperty.GetValue(vm_);
211             if (vValue->IsObject() && !vValue->IsProxy()) {
212                 debuggerProperty->GetValue()->SetObjectId(curObjectId_);
213                 properties_[curObjectId_++] = Global<JSValueRef>(vm_, vValue);
214             }
215         }
216         if (debuggerProperty->HasSymbol()) {
217             debuggerProperty->GetSymbol()->SetObjectId(curObjectId_);
218             properties_[curObjectId_++] = Global<JSValueRef>(vm_, name);
219         }
220         outPropertyDesc->emplace_back(std::move(debuggerProperty));
221     }
222     GetProtoOrProtoType(value, isOwn, isAccessorOnly, outPropertyDesc);
223     GetAdditionalProperties(value, outPropertyDesc);
224 
225     return DispatchResponse::Ok();
226 }
227 
AddTypedArrayRefs(Local<ArrayBufferRef> arrayBufferRef,std::vector<std::unique_ptr<PropertyDescriptor>> * outPropertyDesc)228 void RuntimeImpl::AddTypedArrayRefs(Local<ArrayBufferRef> arrayBufferRef,
229     std::vector<std::unique_ptr<PropertyDescriptor>> *outPropertyDesc)
230 {
231     int32_t arrayBufferByteLength = arrayBufferRef->ByteLength(vm_);
232     int32_t typedArrayLength = arrayBufferByteLength;
233     AddTypedArrayRef<Int8ArrayRef>(arrayBufferRef, typedArrayLength, "[[Int8Array]]", outPropertyDesc);
234     AddTypedArrayRef<Uint8ArrayRef>(arrayBufferRef, typedArrayLength, "[[Uint8Array]]", outPropertyDesc);
235     AddTypedArrayRef<Uint8ClampedArrayRef>(arrayBufferRef, typedArrayLength, "[[Uint8ClampedArray]]", outPropertyDesc);
236 
237     if ((arrayBufferByteLength % NumberSize::BYTES_OF_16BITS) == 0) {
238         typedArrayLength = arrayBufferByteLength / NumberSize::BYTES_OF_16BITS;
239         AddTypedArrayRef<Int16ArrayRef>(arrayBufferRef, typedArrayLength, "[[Int16Array]]", outPropertyDesc);
240         AddTypedArrayRef<Uint16ArrayRef>(arrayBufferRef, typedArrayLength, "[[Uint16Array]]", outPropertyDesc);
241     }
242 
243     if ((arrayBufferByteLength % NumberSize::BYTES_OF_32BITS) == 0) {
244         typedArrayLength = arrayBufferByteLength / NumberSize::BYTES_OF_32BITS;
245         AddTypedArrayRef<Int32ArrayRef>(arrayBufferRef, typedArrayLength, "[[Int32Array]]", outPropertyDesc);
246         AddTypedArrayRef<Uint32ArrayRef>(arrayBufferRef, typedArrayLength, "[[Uint32Array]]", outPropertyDesc);
247         AddTypedArrayRef<Float32ArrayRef>(arrayBufferRef, typedArrayLength, "[[Float32Array]]", outPropertyDesc);
248     }
249 
250     if ((arrayBufferByteLength % NumberSize::BYTES_OF_64BITS) == 0) {
251         typedArrayLength = arrayBufferByteLength / NumberSize::BYTES_OF_64BITS;
252         AddTypedArrayRef<Float64ArrayRef>(arrayBufferRef, typedArrayLength, "[[Float64Array]]", outPropertyDesc);
253     }
254 }
255 
256 template <typename TypedArrayRef>
AddTypedArrayRef(Local<ArrayBufferRef> arrayBufferRef,int32_t length,const char * name,std::vector<std::unique_ptr<PropertyDescriptor>> * outPropertyDesc)257 void RuntimeImpl::AddTypedArrayRef(Local<ArrayBufferRef> arrayBufferRef, int32_t length, const char* name,
258     std::vector<std::unique_ptr<PropertyDescriptor>> *outPropertyDesc)
259 {
260     Local<JSValueRef> jsValueRefTypedArray(TypedArrayRef::New(vm_, arrayBufferRef, 0, length));
261     std::unique_ptr<RemoteObject> remoteObjectTypedArray = RemoteObject::FromTagged(vm_, jsValueRefTypedArray);
262     remoteObjectTypedArray->SetObjectId(curObjectId_);
263     properties_[curObjectId_++] = Global<JSValueRef>(vm_, jsValueRefTypedArray);
264     std::unique_ptr<PropertyDescriptor> debuggerProperty = std::make_unique<PropertyDescriptor>();
265     debuggerProperty->SetName(name)
266         .SetWritable(true)
267         .SetConfigurable(true)
268         .SetEnumerable(false)
269         .SetIsOwn(true)
270         .SetValue(std::move(remoteObjectTypedArray));
271     outPropertyDesc->emplace_back(std::move(debuggerProperty));
272 }
273 
CacheObjectIfNeeded(Local<JSValueRef> valRef,RemoteObject * remoteObj)274 void RuntimeImpl::CacheObjectIfNeeded(Local<JSValueRef> valRef, RemoteObject *remoteObj)
275 {
276     if (valRef->IsObject() && !valRef->IsProxy()) {
277         remoteObj->SetObjectId(curObjectId_);
278         properties_[curObjectId_++] = Global<JSValueRef>(vm_, valRef);
279     }
280 }
281 
GetProtoOrProtoType(Local<JSValueRef> value,bool isOwn,bool isAccessorOnly,std::vector<std::unique_ptr<PropertyDescriptor>> * outPropertyDesc)282 void RuntimeImpl::GetProtoOrProtoType(Local<JSValueRef> value, bool isOwn, bool isAccessorOnly,
283     std::vector<std::unique_ptr<PropertyDescriptor>> *outPropertyDesc)
284 {
285     if (!isAccessorOnly && isOwn && !value->IsProxy()) {
286         return;
287     }
288     // Get Function ProtoOrDynClass
289     if (value->IsConstructor()) {
290         Local<JSValueRef> prototype = Local<FunctionRef>(value)->GetFunctionPrototype(vm_);
291         std::unique_ptr<RemoteObject> protoObj = RemoteObject::FromTagged(vm_, prototype);
292         CacheObjectIfNeeded(prototype, protoObj.get());
293         std::unique_ptr<PropertyDescriptor> debuggerProperty = std::make_unique<PropertyDescriptor>();
294         debuggerProperty->SetName("prototype")
295             .SetWritable(false)
296             .SetConfigurable(false)
297             .SetEnumerable(false)
298             .SetIsOwn(true)
299             .SetValue(std::move(protoObj));
300         outPropertyDesc->emplace_back(std::move(debuggerProperty));
301     }
302     // Get __proto__
303     Local<JSValueRef> proto = Local<ObjectRef>(value)->GetPrototype(vm_);
304     std::unique_ptr<RemoteObject> protoObj = RemoteObject::FromTagged(vm_, proto);
305     CacheObjectIfNeeded(proto, protoObj.get());
306     std::unique_ptr<PropertyDescriptor> debuggerProperty = std::make_unique<PropertyDescriptor>();
307     debuggerProperty->SetName("__proto__")
308         .SetWritable(true)
309         .SetConfigurable(true)
310         .SetEnumerable(false)
311         .SetIsOwn(true)
312         .SetValue(std::move(protoObj));
313     outPropertyDesc->emplace_back(std::move(debuggerProperty));
314 }
315 
GetAdditionalProperties(Local<JSValueRef> value,std::vector<std::unique_ptr<PropertyDescriptor>> * outPropertyDesc)316 void RuntimeImpl::GetAdditionalProperties(Local<JSValueRef> value,
317     std::vector<std::unique_ptr<PropertyDescriptor>> *outPropertyDesc)
318 {
319     // The length of the TypedArray have to be limited(less than or equal to lengthTypedArrayLimit) until we construct
320     // the PropertyPreview class. Let lengthTypedArrayLimit be 10000 temporarily.
321     static const uint32_t lengthTypedArrayLimit = 10000;
322 
323     // The width of the string-expression for JSTypedArray::MAX_TYPED_ARRAY_INDEX which is euqal to
324     // JSObject::MAX_ELEMENT_INDEX which is equal to std::numeric_limits<uint32_t>::max(). (42,9496,7295)
325     static const int32_t widthStrExprMaxElementIndex = 10;
326 
327     if (value->IsTypedArray()) {
328         Local<TypedArrayRef> localTypedArrayRef(value);
329         uint32_t lengthTypedArray = localTypedArrayRef->ArrayLength(vm_);
330         if (lengthTypedArray > lengthTypedArrayLimit) {
331             LOG(ERROR, DEBUGGER) << "The length of the TypedArray is non-compliant or unsupported.";
332             return;
333         }
334         for (uint32_t i = 0; i < lengthTypedArray; i++) {
335             Local<JSValueRef> localValRefElement = localTypedArrayRef->Get(vm_, i);
336             std::unique_ptr<RemoteObject> remoteObjElement = RemoteObject::FromTagged(vm_, localValRefElement);
337             remoteObjElement->SetObjectId(curObjectId_);
338             properties_[curObjectId_++] = Global<JSValueRef>(vm_, localValRefElement);
339             std::unique_ptr<PropertyDescriptor> debuggerProperty = std::make_unique<PropertyDescriptor>();
340 
341             std::ostringstream osNameElement;
342             osNameElement << std::right << std::setw(widthStrExprMaxElementIndex) << i;
343             std::string cStrNameElement = osNameElement.str();
344             debuggerProperty->SetName(cStrNameElement)
345                 .SetWritable(true)
346                 .SetConfigurable(true)
347                 .SetEnumerable(false)
348                 .SetIsOwn(true)
349                 .SetValue(std::move(remoteObjElement));
350             outPropertyDesc->emplace_back(std::move(debuggerProperty));
351         }
352     }
353 }
354 }  // namespace panda::ecmascript::tooling