/* * Copyright (c) 2021 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ecmascript/tooling/agent/runtime_impl.h" #include #include "ecmascript/napi/include/dfx_jsnapi.h" #include "ecmascript/tooling/base/pt_returns.h" #include "ecmascript/tooling/protocol_channel.h" #include "libpandabase/utils/logger.h" namespace panda::ecmascript::tooling { void RuntimeImpl::DispatcherImpl::Dispatch(const DispatchRequest &request) { static std::unordered_map dispatcherTable { { "enable", &RuntimeImpl::DispatcherImpl::Enable }, { "getProperties", &RuntimeImpl::DispatcherImpl::GetProperties }, { "runIfWaitingForDebugger", &RuntimeImpl::DispatcherImpl::RunIfWaitingForDebugger }, { "callFunctionOn", &RuntimeImpl::DispatcherImpl::CallFunctionOn }, { "getHeapUsage", &RuntimeImpl::DispatcherImpl::GetHeapUsage } }; const std::string &method = request.GetMethod(); LOG(DEBUG, DEBUGGER) << "dispatch [" << method << "] to RuntimeImpl"; auto entry = dispatcherTable.find(method); if (entry != dispatcherTable.end()) { (this->*(entry->second))(request); } else { LOG(ERROR, DEBUGGER) << "unknown method: " << method; SendResponse(request, DispatchResponse::Fail("unknown method: " + method)); } } void RuntimeImpl::DispatcherImpl::Enable(const DispatchRequest &request) { DispatchResponse response = runtime_->Enable(); SendResponse(request, response); } void RuntimeImpl::DispatcherImpl::Disable(const DispatchRequest &request) { DispatchResponse response = runtime_->Disable(); SendResponse(request, response); } void RuntimeImpl::DispatcherImpl::RunIfWaitingForDebugger(const DispatchRequest &request) { DispatchResponse response = runtime_->RunIfWaitingForDebugger(); SendResponse(request, response); } void RuntimeImpl::DispatcherImpl::GetProperties(const DispatchRequest &request) { std::unique_ptr params = GetPropertiesParams::Create(request.GetParams()); if (params == nullptr) { SendResponse(request, DispatchResponse::Fail("wrong params")); return; } std::vector> outPropertyDesc; std::optional>> outInternalDescs; std::optional>> outPrivateProperties; std::optional> outExceptionDetails; DispatchResponse response = runtime_->GetProperties(*params, &outPropertyDesc, &outInternalDescs, &outPrivateProperties, &outExceptionDetails); if (outExceptionDetails) { LOG(WARNING, DEBUGGER) << "GetProperties thrown an exception"; } GetPropertiesReturns result(std::move(outPropertyDesc), std::move(outInternalDescs), std::move(outPrivateProperties), std::move(outExceptionDetails)); SendResponse(request, response, result); } void RuntimeImpl::DispatcherImpl::CallFunctionOn(const DispatchRequest &request) { std::unique_ptr params = CallFunctionOnParams::Create(request.GetParams()); if (params == nullptr) { SendResponse(request, DispatchResponse::Fail("wrong params")); return; } std::unique_ptr outRemoteObject; std::optional> outExceptionDetails; DispatchResponse response = runtime_->CallFunctionOn(*params, &outRemoteObject, &outExceptionDetails); if (outExceptionDetails) { LOG(WARNING, DEBUGGER) << "CallFunctionOn thrown an exception"; } CallFunctionOnReturns result(std::move(outRemoteObject), std::move(outExceptionDetails)); SendResponse(request, response, result); } void RuntimeImpl::DispatcherImpl::GetHeapUsage(const DispatchRequest &request) { double usedSize = 0; double totalSize = 0; DispatchResponse response = runtime_->GetHeapUsage(&usedSize, &totalSize); GetHeapUsageReturns result(usedSize, totalSize); SendResponse(request, response, result); } bool RuntimeImpl::Frontend::AllowNotify() const { return channel_ != nullptr; } void RuntimeImpl::Frontend::RunIfWaitingForDebugger() { if (!AllowNotify()) { return; } channel_->RunIfWaitingForDebugger(); } DispatchResponse RuntimeImpl::Enable() { return DispatchResponse::Ok(); } DispatchResponse RuntimeImpl::Disable() { return DispatchResponse::Ok(); } DispatchResponse RuntimeImpl::RunIfWaitingForDebugger() { frontend_.RunIfWaitingForDebugger(); return DispatchResponse::Ok(); } DispatchResponse RuntimeImpl::CallFunctionOn([[maybe_unused]] const CallFunctionOnParams ¶ms, std::unique_ptr *outRemoteObject, [[maybe_unused]] std::optional> *outExceptionDetails) { // Return EvalError temporarily. auto error = Exception::EvalError(vm_, StringRef::NewFromUtf8(vm_, "Unsupport eval now")); *outRemoteObject = RemoteObject::FromTagged(vm_, error); return DispatchResponse::Ok(); } DispatchResponse RuntimeImpl::GetHeapUsage(double *usedSize, double *totalSize) { *totalSize = static_cast(DFXJSNApi::GetHeapTotalSize(vm_)); *usedSize = static_cast(DFXJSNApi::GetHeapUsedSize(vm_)); return DispatchResponse::Ok(); } DispatchResponse RuntimeImpl::GetProperties(const GetPropertiesParams ¶ms, std::vector> *outPropertyDesc, [[maybe_unused]] std::optional>> *outInternalDescs, [[maybe_unused]] std::optional>> *outPrivateProps, [[maybe_unused]] std::optional> *outExceptionDetails) { RemoteObjectId objectId = params.GetObjectId(); bool isOwn = params.GetOwnProperties(); bool isAccessorOnly = params.GetAccessPropertiesOnly(); auto iter = properties_.find(objectId); if (iter == properties_.end()) { LOG(ERROR, DEBUGGER) << "RuntimeImpl::GetProperties Unknown object id: " << objectId; return DispatchResponse::Fail("Unknown object id"); } Local value = Local(vm_, iter->second); if (value.IsEmpty() || !value->IsObject()) { LOG(ERROR, DEBUGGER) << "RuntimeImpl::GetProperties should a js object"; return DispatchResponse::Fail("Not a object"); } if (value->IsArrayBuffer()) { Local arrayBufferRef(value); AddTypedArrayRefs(arrayBufferRef, outPropertyDesc); } Local keys = Local(value)->GetOwnPropertyNames(vm_); int32_t length = keys->Length(vm_); Local name = JSValueRef::Undefined(vm_); for (int32_t i = 0; i < length; ++i) { name = keys->Get(vm_, i); PropertyAttribute jsProperty = PropertyAttribute::Default(); if (!Local(value)->GetOwnProperty(vm_, name, jsProperty)) { continue; } std::unique_ptr debuggerProperty = PropertyDescriptor::FromProperty(vm_, name, jsProperty); if (isAccessorOnly && !jsProperty.HasGetter() && !jsProperty.HasSetter()) { continue; } if (debuggerProperty->HasGet()) { debuggerProperty->GetGet()->SetObjectId(curObjectId_); properties_[curObjectId_++] = Global(vm_, jsProperty.GetGetter(vm_)); } if (debuggerProperty->HasSet()) { debuggerProperty->GetSet()->SetObjectId(curObjectId_); properties_[curObjectId_++] = Global(vm_, jsProperty.GetSetter(vm_)); } if (debuggerProperty->HasValue()) { Local vValue = jsProperty.GetValue(vm_); if (vValue->IsObject() && !vValue->IsProxy()) { debuggerProperty->GetValue()->SetObjectId(curObjectId_); properties_[curObjectId_++] = Global(vm_, vValue); } } if (debuggerProperty->HasSymbol()) { debuggerProperty->GetSymbol()->SetObjectId(curObjectId_); properties_[curObjectId_++] = Global(vm_, name); } outPropertyDesc->emplace_back(std::move(debuggerProperty)); } GetProtoOrProtoType(value, isOwn, isAccessorOnly, outPropertyDesc); GetAdditionalProperties(value, outPropertyDesc); return DispatchResponse::Ok(); } void RuntimeImpl::AddTypedArrayRefs(Local arrayBufferRef, std::vector> *outPropertyDesc) { int32_t arrayBufferByteLength = arrayBufferRef->ByteLength(vm_); int32_t typedArrayLength = arrayBufferByteLength; AddTypedArrayRef(arrayBufferRef, typedArrayLength, "[[Int8Array]]", outPropertyDesc); AddTypedArrayRef(arrayBufferRef, typedArrayLength, "[[Uint8Array]]", outPropertyDesc); AddTypedArrayRef(arrayBufferRef, typedArrayLength, "[[Uint8ClampedArray]]", outPropertyDesc); if ((arrayBufferByteLength % NumberSize::BYTES_OF_16BITS) == 0) { typedArrayLength = arrayBufferByteLength / NumberSize::BYTES_OF_16BITS; AddTypedArrayRef(arrayBufferRef, typedArrayLength, "[[Int16Array]]", outPropertyDesc); AddTypedArrayRef(arrayBufferRef, typedArrayLength, "[[Uint16Array]]", outPropertyDesc); } if ((arrayBufferByteLength % NumberSize::BYTES_OF_32BITS) == 0) { typedArrayLength = arrayBufferByteLength / NumberSize::BYTES_OF_32BITS; AddTypedArrayRef(arrayBufferRef, typedArrayLength, "[[Int32Array]]", outPropertyDesc); AddTypedArrayRef(arrayBufferRef, typedArrayLength, "[[Uint32Array]]", outPropertyDesc); AddTypedArrayRef(arrayBufferRef, typedArrayLength, "[[Float32Array]]", outPropertyDesc); } if ((arrayBufferByteLength % NumberSize::BYTES_OF_64BITS) == 0) { typedArrayLength = arrayBufferByteLength / NumberSize::BYTES_OF_64BITS; AddTypedArrayRef(arrayBufferRef, typedArrayLength, "[[Float64Array]]", outPropertyDesc); } } template void RuntimeImpl::AddTypedArrayRef(Local arrayBufferRef, int32_t length, const char* name, std::vector> *outPropertyDesc) { Local jsValueRefTypedArray(TypedArrayRef::New(vm_, arrayBufferRef, 0, length)); std::unique_ptr remoteObjectTypedArray = RemoteObject::FromTagged(vm_, jsValueRefTypedArray); remoteObjectTypedArray->SetObjectId(curObjectId_); properties_[curObjectId_++] = Global(vm_, jsValueRefTypedArray); std::unique_ptr debuggerProperty = std::make_unique(); debuggerProperty->SetName(name) .SetWritable(true) .SetConfigurable(true) .SetEnumerable(false) .SetIsOwn(true) .SetValue(std::move(remoteObjectTypedArray)); outPropertyDesc->emplace_back(std::move(debuggerProperty)); } void RuntimeImpl::CacheObjectIfNeeded(Local valRef, RemoteObject *remoteObj) { if (valRef->IsObject() && !valRef->IsProxy()) { remoteObj->SetObjectId(curObjectId_); properties_[curObjectId_++] = Global(vm_, valRef); } } void RuntimeImpl::GetProtoOrProtoType(Local value, bool isOwn, bool isAccessorOnly, std::vector> *outPropertyDesc) { if (!isAccessorOnly && isOwn && !value->IsProxy()) { return; } // Get Function ProtoOrDynClass if (value->IsConstructor()) { Local prototype = Local(value)->GetFunctionPrototype(vm_); std::unique_ptr protoObj = RemoteObject::FromTagged(vm_, prototype); CacheObjectIfNeeded(prototype, protoObj.get()); std::unique_ptr debuggerProperty = std::make_unique(); debuggerProperty->SetName("prototype") .SetWritable(false) .SetConfigurable(false) .SetEnumerable(false) .SetIsOwn(true) .SetValue(std::move(protoObj)); outPropertyDesc->emplace_back(std::move(debuggerProperty)); } // Get __proto__ Local proto = Local(value)->GetPrototype(vm_); std::unique_ptr protoObj = RemoteObject::FromTagged(vm_, proto); CacheObjectIfNeeded(proto, protoObj.get()); std::unique_ptr debuggerProperty = std::make_unique(); debuggerProperty->SetName("__proto__") .SetWritable(true) .SetConfigurable(true) .SetEnumerable(false) .SetIsOwn(true) .SetValue(std::move(protoObj)); outPropertyDesc->emplace_back(std::move(debuggerProperty)); } void RuntimeImpl::GetAdditionalProperties(Local value, std::vector> *outPropertyDesc) { // The length of the TypedArray have to be limited(less than or equal to lengthTypedArrayLimit) until we construct // the PropertyPreview class. Let lengthTypedArrayLimit be 10000 temporarily. static const uint32_t lengthTypedArrayLimit = 10000; // The width of the string-expression for JSTypedArray::MAX_TYPED_ARRAY_INDEX which is euqal to // JSObject::MAX_ELEMENT_INDEX which is equal to std::numeric_limits::max(). (42,9496,7295) static const int32_t widthStrExprMaxElementIndex = 10; if (value->IsTypedArray()) { Local localTypedArrayRef(value); uint32_t lengthTypedArray = localTypedArrayRef->ArrayLength(vm_); if (lengthTypedArray > lengthTypedArrayLimit) { LOG(ERROR, DEBUGGER) << "The length of the TypedArray is non-compliant or unsupported."; return; } for (uint32_t i = 0; i < lengthTypedArray; i++) { Local localValRefElement = localTypedArrayRef->Get(vm_, i); std::unique_ptr remoteObjElement = RemoteObject::FromTagged(vm_, localValRefElement); remoteObjElement->SetObjectId(curObjectId_); properties_[curObjectId_++] = Global(vm_, localValRefElement); std::unique_ptr debuggerProperty = std::make_unique(); std::ostringstream osNameElement; osNameElement << std::right << std::setw(widthStrExprMaxElementIndex) << i; std::string cStrNameElement = osNameElement.str(); debuggerProperty->SetName(cStrNameElement) .SetWritable(true) .SetConfigurable(true) .SetEnumerable(false) .SetIsOwn(true) .SetValue(std::move(remoteObjElement)); outPropertyDesc->emplace_back(std::move(debuggerProperty)); } } } } // namespace panda::ecmascript::tooling