From 2a48144d6a94f98512903c344a7bcd135d728832 Mon Sep 17 00:00:00 2001 From: landerlyoung Date: Sat, 1 May 2021 15:56:09 +0800 Subject: [PATCH 01/10] add python backend --- backend/Python/CMakeLists.txt | 12 +- backend/Python/PyEngine.cc | 59 +++++++ backend/Python/PyEngine.h | 115 ++++++++++++++ backend/Python/PyException.cc | 40 +++++ backend/Python/PyLocalReference.cc | 216 ++++++++++++++++++++++++++ backend/Python/PyNative.cc | 47 ++++++ backend/Python/PyNative.hpp | 33 ++++ backend/Python/PyReference.hpp | 142 +++++++++++++++++ backend/Python/PyUtils.cc | 46 ++++++ backend/Python/PyValue.cc | 82 ++++++++++ backend/Python/trait/TraitEngine.h | 36 +++++ backend/Python/trait/TraitException.h | 36 +++++ backend/Python/trait/TraitIncludes.h | 26 ++++ backend/Python/trait/TraitNative.h | 46 ++++++ backend/Python/trait/TraitReference.h | 41 +++++ backend/Python/trait/TraitScope.h | 80 ++++++++++ backend/Python/trait/TraitUtils.h | 32 ++++ test/cmake/TestEnv.cmake | 5 + 18 files changed, 1093 insertions(+), 1 deletion(-) create mode 100644 backend/Python/PyEngine.cc create mode 100644 backend/Python/PyEngine.h create mode 100644 backend/Python/PyException.cc create mode 100644 backend/Python/PyLocalReference.cc create mode 100644 backend/Python/PyNative.cc create mode 100644 backend/Python/PyNative.hpp create mode 100644 backend/Python/PyReference.hpp create mode 100644 backend/Python/PyUtils.cc create mode 100644 backend/Python/PyValue.cc create mode 100644 backend/Python/trait/TraitEngine.h create mode 100644 backend/Python/trait/TraitException.h create mode 100644 backend/Python/trait/TraitIncludes.h create mode 100644 backend/Python/trait/TraitNative.h create mode 100644 backend/Python/trait/TraitReference.h create mode 100644 backend/Python/trait/TraitScope.h create mode 100644 backend/Python/trait/TraitUtils.h diff --git a/backend/Python/CMakeLists.txt b/backend/Python/CMakeLists.txt index 6d96b276..d547ca57 100644 --- a/backend/Python/CMakeLists.txt +++ b/backend/Python/CMakeLists.txt @@ -1 +1,11 @@ -message(FATAL_ERROR "${SCRIPTX_BACKEND} is to be implemented.") +target_sources(ScriptX PRIVATE + ${CMAKE_CURRENT_LIST_DIR}/PyEngine.cc + ${CMAKE_CURRENT_LIST_DIR}/PyEngine.h + ${CMAKE_CURRENT_LIST_DIR}/PyException.cc + ${CMAKE_CURRENT_LIST_DIR}/PyLocalReference.cc + ${CMAKE_CURRENT_LIST_DIR}/PyNative.cc + ${CMAKE_CURRENT_LIST_DIR}/PyNative.hpp + ${CMAKE_CURRENT_LIST_DIR}/PyReference.hpp + ${CMAKE_CURRENT_LIST_DIR}/PyUtils.cc + ${CMAKE_CURRENT_LIST_DIR}/PyValue.cc + ) \ No newline at end of file diff --git a/backend/Python/PyEngine.cc b/backend/Python/PyEngine.cc new file mode 100644 index 00000000..157cd0e8 --- /dev/null +++ b/backend/Python/PyEngine.cc @@ -0,0 +1,59 @@ +/* + * Tencent is pleased to support the open source community by making ScriptX available. + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * 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 "PyEngine.h" +#include "../../src/Utils.h" + +namespace script::py_backend { + +PyEngine::PyEngine(std::shared_ptr queue) {} + +PyEngine::PyEngine() : PyEngine(std::shared_ptr{}) {} + +PyEngine::~PyEngine() = default; + +void PyEngine::destroy() noexcept {} + +Local PyEngine::get(const Local& key) { return Local(); } + +void PyEngine::set(const Local& key, const Local& value) {} + +Local PyEngine::eval(const Local& script) { return eval(script, Local()); } + +Local PyEngine::eval(const Local& script, const Local& sourceFile) { + return eval(script, sourceFile.asValue()); +} + +Local PyEngine::eval(const Local& script, const Local& sourceFile) { + return Local(); +} + +std::shared_ptr PyEngine::messageQueue() { + return std::shared_ptr(); +} + +void PyEngine::gc() {} + +void PyEngine::adjustAssociatedMemory(int64_t count) {} + +ScriptLanguage PyEngine::getLanguageType() { return ScriptLanguage::kJavaScript; } + +std::string PyEngine::getEngineVersion() { return ""; } + +bool PyEngine::isDestroying() const { return false; } + +} // namespace script::py_backend diff --git a/backend/Python/PyEngine.h b/backend/Python/PyEngine.h new file mode 100644 index 00000000..21df182d --- /dev/null +++ b/backend/Python/PyEngine.h @@ -0,0 +1,115 @@ +/* + * Tencent is pleased to support the open source community by making ScriptX available. + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * 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. + */ + +#pragma once + +#include "../../src/Engine.h" +#include "../../src/Exception.h" +#include "../../src/utils/MessageQueue.h" + +namespace script::py_backend { + +class PyEngine : public ScriptEngine { + protected: + public: + PyEngine(std::shared_ptr<::script::utils::MessageQueue> queue); + + PyEngine(); + + SCRIPTX_DISALLOW_COPY_AND_MOVE(PyEngine); + + void destroy() noexcept override; + + bool isDestroying() const override; + + Local get(const Local& key) override; + + void set(const Local& key, const Local& value) override; + using ScriptEngine::set; + + Local eval(const Local& script, const Local& sourceFile); + Local eval(const Local& script, const Local& sourceFile) override; + Local eval(const Local& script) override; + using ScriptEngine::eval; + + std::shared_ptr messageQueue() override; + + void gc() override; + + void adjustAssociatedMemory(int64_t count) override; + + ScriptLanguage getLanguageType() override; + + std::string getEngineVersion() override; + + protected: + ~PyEngine() override; + + private: + template + bool registerNativeClassImpl(const ClassDefine* classDefine) { + return false; + } + + Local getNamespaceForRegister(const std::string_view& nameSpace) { + TEMPLATE_NOT_IMPLEMENTED(); + } + + template + Local newNativeClassImpl(const ClassDefine* classDefine, size_t size, + const Local* args) { + TEMPLATE_NOT_IMPLEMENTED(); + } + + template + bool isInstanceOfImpl(const Local& value, const ClassDefine* classDefine) { + return false; + } + + template + T* getNativeInstanceImpl(const Local& value, const ClassDefine* classDefine) { + return nullptr; + } + + private: + template + friend class ::script::Local; + + template + friend class ::script::Global; + + template + friend class ::script::Weak; + + friend class ::script::Object; + + friend class ::script::Array; + + friend class ::script::Function; + + friend class ::script::ByteBuffer; + + friend class ::script::ScriptEngine; + + friend class ::script::Exception; + + friend class ::script::Arguments; + + friend class ::script::ScriptClass; +}; + +} // namespace script::py_backend \ No newline at end of file diff --git a/backend/Python/PyException.cc b/backend/Python/PyException.cc new file mode 100644 index 00000000..871a0f3c --- /dev/null +++ b/backend/Python/PyException.cc @@ -0,0 +1,40 @@ +/* + * Tencent is pleased to support the open source community by making ScriptX available. + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * 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 + +namespace script { + +Exception::Exception(std::string msg) : std::exception(), exception_({}) { + exception_.message_ = std::move(msg); +} + +Exception::Exception(const script::Local& message) + : std::exception(), exception_() {} + +Exception::Exception(const script::Local& exception) + : std::exception(), exception_({}) {} + +Local Exception::exception() const { return {}; } + +std::string Exception::message() const noexcept { return exception_.message_; } + +std::string Exception::stacktrace() const noexcept { return "[no stacktrace]"; } + +const char* Exception::what() const noexcept { return exception_.message_.c_str(); } + +} // namespace script diff --git a/backend/Python/PyLocalReference.cc b/backend/Python/PyLocalReference.cc new file mode 100644 index 00000000..d4f202d8 --- /dev/null +++ b/backend/Python/PyLocalReference.cc @@ -0,0 +1,216 @@ +/* + * Tencent is pleased to support the open source community by making ScriptX available. + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * 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 + +namespace script { + +#define REF_IMPL_BASIC_FUNC(ValueType) \ + Local::Local(const Local& copy) : val_(copy.val_) {} \ + Local::Local(Local&& move) noexcept : val_(move.val_) {} \ + Local::~Local() {} \ + Local& Local::operator=(const Local& from) { \ + Local(from).swap(*this); \ + return *this; \ + } \ + Local& Local::operator=(Local&& move) noexcept { \ + Local(std::move(move)).swap(*this); \ + return *this; \ + } \ + void Local::swap(Local& rhs) noexcept { std::swap(val_, rhs.val_); } + +#define REF_IMPL_BASIC_EQUALS(ValueType) \ + bool Local::operator==(const script::Local& other) const { \ + return asValue() == other; \ + } + +#define REF_IMPL_BASIC_NOT_VALUE(ValueType) \ + Local::Local(InternalLocalRef val) : val_(val) {} \ + Local Local::describe() const { return asValue().describe(); } \ + std::string Local::describeUtf8() const { return asValue().describeUtf8(); } + +#define REF_IMPL_TO_VALUE(ValueType) \ + Local Local::asValue() const { return Local(/*TMPL: value*/); } + +REF_IMPL_BASIC_FUNC(Value) + +REF_IMPL_BASIC_FUNC(Object) +REF_IMPL_BASIC_NOT_VALUE(Object) +REF_IMPL_BASIC_EQUALS(Object) +REF_IMPL_TO_VALUE(Object) + +REF_IMPL_BASIC_FUNC(String) +REF_IMPL_BASIC_NOT_VALUE(String) +REF_IMPL_BASIC_EQUALS(String) +REF_IMPL_TO_VALUE(String) + +REF_IMPL_BASIC_FUNC(Number) +REF_IMPL_BASIC_NOT_VALUE(Number) +REF_IMPL_BASIC_EQUALS(Number) +REF_IMPL_TO_VALUE(Number) + +REF_IMPL_BASIC_FUNC(Boolean) +REF_IMPL_BASIC_NOT_VALUE(Boolean) +REF_IMPL_BASIC_EQUALS(Boolean) +REF_IMPL_TO_VALUE(Boolean) + +REF_IMPL_BASIC_FUNC(Function) +REF_IMPL_BASIC_NOT_VALUE(Function) +REF_IMPL_BASIC_EQUALS(Function) +REF_IMPL_TO_VALUE(Function) + +REF_IMPL_BASIC_FUNC(Array) +REF_IMPL_BASIC_NOT_VALUE(Array) +REF_IMPL_BASIC_EQUALS(Array) +REF_IMPL_TO_VALUE(Array) + +REF_IMPL_BASIC_FUNC(ByteBuffer) +REF_IMPL_BASIC_NOT_VALUE(ByteBuffer) +REF_IMPL_BASIC_EQUALS(ByteBuffer) +REF_IMPL_TO_VALUE(ByteBuffer) + +REF_IMPL_BASIC_FUNC(Unsupported) +REF_IMPL_BASIC_NOT_VALUE(Unsupported) +REF_IMPL_BASIC_EQUALS(Unsupported) +REF_IMPL_TO_VALUE(Unsupported) + +// ==== value ==== + +Local::Local() noexcept : val_() {} + +Local::Local(InternalLocalRef v8Local) : val_(v8Local) {} + +bool Local::isNull() const { return false; } + +void Local::reset() {} + +ValueKind Local::getKind() const { + if (isNull()) { + return ValueKind::kNull; + } else if (isString()) { + return ValueKind::kString; + } else if (isNumber()) { + return ValueKind::kNumber; + } else if (isBoolean()) { + return ValueKind::kBoolean; + } else if (isFunction()) { + return ValueKind::kFunction; + } else if (isArray()) { + return ValueKind::kArray; + } else if (isByteBuffer()) { + return ValueKind::kByteBuffer; + } else if (isObject()) { + return ValueKind::kObject; + } else { + return ValueKind::kUnsupported; + } +} + +bool Local::isString() const { return false; } + +bool Local::isNumber() const { return false; } + +bool Local::isBoolean() const { return false; } + +bool Local::isFunction() const { return false; } + +bool Local::isArray() const { return false; } + +bool Local::isByteBuffer() const { return false; } + +bool Local::isObject() const { return false; } + +bool Local::isUnsupported() const { return false; } + +Local Local::asString() const { throw Exception("can't cast value as String"); } + +Local Local::asNumber() const { throw Exception("can't cast value as Number"); } + +Local Local::asBoolean() const { throw Exception("can't cast value as Boolean"); } + +Local Local::asFunction() const { + throw Exception("can't cast value as Function"); +} + +Local Local::asArray() const { throw Exception("can't cast value as Array"); } + +Local Local::asByteBuffer() const { + throw Exception("can't cast value as ByteBuffer"); +} + +Local Local::asObject() const { throw Exception("can't cast value as Object"); } + +Local Local::asUnsupported() const { + throw Exception("can't cast value as Unsupported"); +} + +bool Local::operator==(const script::Local& other) const { return false; } + +Local Local::describe() const { TEMPLATE_NOT_IMPLEMENTED(); } + +Local Local::get(const script::Local& key) const { return {}; } + +void Local::set(const script::Local& key, + const script::Local& value) const {} + +void Local::remove(const Local& key) const {} +bool Local::has(const Local& key) const { return true; } + +bool Local::instanceOf(const Local& type) const { return false; } + +std::vector> Local::getKeys() const { return {}; } + +float Local::toFloat() const { return static_cast(toDouble()); } + +double Local::toDouble() const { return 0; } + +int32_t Local::toInt32() const { return static_cast(toDouble()); } + +int64_t Local::toInt64() const { return static_cast(toDouble()); } + +bool Local::value() const { return false; } + +Local Local::callImpl(const Local& thiz, size_t size, + const Local* args) const { + return {}; +} + +size_t Local::size() const { return 0; } + +Local Local::get(size_t index) const { return {}; } + +void Local::set(size_t index, const script::Local& value) const {} + +void Local::add(const script::Local& value) const { set(size(), value); } + +void Local::clear() const {} + +ByteBuffer::Type Local::getType() const { return ByteBuffer::Type::KFloat32; } + +bool Local::isShared() const { return true; } + +void Local::commit() const {} + +void Local::sync() const {} + +size_t Local::byteLength() const { return 0; } + +void* Local::getRawBytes() const { return nullptr; } + +std::shared_ptr Local::getRawBytesShared() const { return {}; } + +} // namespace script diff --git a/backend/Python/PyNative.cc b/backend/Python/PyNative.cc new file mode 100644 index 00000000..05c10e6a --- /dev/null +++ b/backend/Python/PyNative.cc @@ -0,0 +1,47 @@ +/* + * Tencent is pleased to support the open source community by making ScriptX available. + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * 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 + +namespace script { + +Arguments::Arguments(InternalCallbackInfoType callbackInfo) : callbackInfo_(callbackInfo) {} + +Arguments::~Arguments() = default; + +Local Arguments::thiz() const { TEMPLATE_NOT_IMPLEMENTED(); } + +bool Arguments::hasThiz() const { TEMPLATE_NOT_IMPLEMENTED(); } + +size_t Arguments::size() const { TEMPLATE_NOT_IMPLEMENTED(); } + +Local Arguments::operator[](size_t i) const { return {}; } + +ScriptEngine* Arguments::engine() const { return nullptr; } + +ScriptClass::ScriptClass(const script::Local& scriptObject) : internalState_() { + TEMPLATE_NOT_IMPLEMENTED(); +} + +Local ScriptClass::getScriptObject() const { TEMPLATE_NOT_IMPLEMENTED(); } + +Local ScriptClass::getInternalStore() const { TEMPLATE_NOT_IMPLEMENTED(); } + +ScriptEngine* ScriptClass::getScriptEngine() const { TEMPLATE_NOT_IMPLEMENTED(); } + +ScriptClass::~ScriptClass() = default; +} // namespace script \ No newline at end of file diff --git a/backend/Python/PyNative.hpp b/backend/Python/PyNative.hpp new file mode 100644 index 00000000..ec9c2626 --- /dev/null +++ b/backend/Python/PyNative.hpp @@ -0,0 +1,33 @@ +/* + * Tencent is pleased to support the open source community by making ScriptX available. + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * 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. + */ + +#pragma once + +#include "../../src/Native.h" +#include "PyEngine.h" + +namespace script { + +template +ScriptClass::ScriptClass(const ScriptClass::ConstructFromCpp) : internalState_() {} + +template +T* Arguments::engineAs() const { + return nullptr; +} + +} // namespace script \ No newline at end of file diff --git a/backend/Python/PyReference.hpp b/backend/Python/PyReference.hpp new file mode 100644 index 00000000..4c7fdb5d --- /dev/null +++ b/backend/Python/PyReference.hpp @@ -0,0 +1,142 @@ +/* + * Tencent is pleased to support the open source community by making ScriptX available. + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * 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. + */ + +#pragma once +#include + +namespace script { + +template +Global::Global() noexcept : val_() {} + +template +Global::Global(const script::Local& localReference) {} + +template +Global::Global(const script::Weak& weak) {} + +template +Global::Global(const script::Global& copy) : val_(copy.val_) {} + +template +Global::Global(script::Global&& move) noexcept : val_(move.val_) {} + +template +Global::~Global() {} + +template +Global& Global::operator=(const script::Global& assign) { + Global(assign).swap(*this); + return *this; +} + +template +Global& Global::operator=(script::Global&& move) noexcept { + Global(std::move(move)).swap(*this); + return *this; +} + +template +void Global::swap(Global& rhs) noexcept {} + +template +Global& Global::operator=(const script::Local& assign) { + *this = Global(assign); + return *this; +} + +template +Local Global::get() const { + TEMPLATE_NOT_IMPLEMENTED(); +} + +template +Local Global::getValue() const { + TEMPLATE_NOT_IMPLEMENTED(); +} + +template +bool Global::isEmpty() const { + return false; +} + +template +void Global::reset() {} + +// == Weak == + +template +Weak::Weak() noexcept : val_() {} + +template +Weak::~Weak() {} + +template +Weak::Weak(const script::Local& localReference) {} + +template +Weak::Weak(const script::Global& globalReference) {} + +template +Weak::Weak(const script::Weak& copy) : val_(copy.val_) {} + +template +Weak::Weak(script::Weak&& move) noexcept : val_(std::move(move.val_)) {} + +template +Weak& Weak::operator=(const script::Weak& assign) { + val_ = assign.val_; + return *this; +} + +template +Weak& Weak::operator=(script::Weak&& move) noexcept { + val_ = std::move(move.val_); + return *this; +} + +template +void Weak::swap(Weak& rhs) noexcept { + std::swap(val_, rhs.val_); +} + +template +Weak& Weak::operator=(const script::Local& assign) { + *this = Weak(assign); + return *this; +} + +template +Local Weak::get() const { + if (isEmpty()) throw Exception("get on empty Weak"); + TEMPLATE_NOT_IMPLEMENTED(); +} + +template +Local Weak::getValue() const { + TEMPLATE_NOT_IMPLEMENTED(); +} + +template +bool Weak::isEmpty() const { + return false; +} + +template +void Weak::reset() noexcept {} + +} // namespace script diff --git a/backend/Python/PyUtils.cc b/backend/Python/PyUtils.cc new file mode 100644 index 00000000..cd4bf063 --- /dev/null +++ b/backend/Python/PyUtils.cc @@ -0,0 +1,46 @@ +/* + * Tencent is pleased to support the open source community by making ScriptX available. + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * 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 + +namespace script { + +StringHolder::StringHolder(const script::Local &string) {} + +StringHolder::~StringHolder() = default; + +size_t StringHolder::length() const { return 0; } + +const char *StringHolder::c_str() const { return ""; } + +std::string_view StringHolder::stringView() const { return {}; } + +std::string StringHolder::string() const { return {}; } + +#if defined(__cpp_char8_t) +// NOLINTNEXTLINE(clang-analyzer-cplusplus.InnerPointer) +std::u8string StringHolder::u8string() const { return std::u8string(c_u8str(), length()); } + +std::u8string_view StringHolder::u8stringView() const { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.InnerPointer) + return std::u8string_view(c_u8str(), length()); +} + +const char8_t *StringHolder::c_u8str() const { return reinterpret_cast(c_str()); } +#endif + +} // namespace script diff --git a/backend/Python/PyValue.cc b/backend/Python/PyValue.cc new file mode 100644 index 00000000..548ad154 --- /dev/null +++ b/backend/Python/PyValue.cc @@ -0,0 +1,82 @@ +/* + * Tencent is pleased to support the open source community by making ScriptX available. + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * 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 "../../src/Exception.h" +#include "../../src/Reference.h" +#include "../../src/Scope.h" +#include "../../src/Value.h" + +namespace script { + +Local Object::newObject() { TEMPLATE_NOT_IMPLEMENTED(); } + +Local Object::newObjectImpl(const Local& type, size_t size, + const Local* args) { + TEMPLATE_NOT_IMPLEMENTED(); +} + +Local String::newString(const char* utf8) { TEMPLATE_NOT_IMPLEMENTED(); } + +Local String::newString(std::string_view utf8) { TEMPLATE_NOT_IMPLEMENTED(); } + +Local String::newString(const std::string& utf8) { TEMPLATE_NOT_IMPLEMENTED(); } + +#if defined(__cpp_char8_t) + +Local String::newString(const char8_t* utf8) { + return newString(reinterpret_cast(utf8)); +} + +Local String::newString(std::u8string_view utf8) { + return newString(std::string_view(reinterpret_cast(utf8.data()), utf8.length())); +} + +Local String::newString(const std::u8string& utf8) { return newString(utf8.c_str()); } + +#endif + +Local Number::newNumber(float value) { return newNumber(static_cast(value)); } + +Local Number::newNumber(double value) { TEMPLATE_NOT_IMPLEMENTED(); } + +Local Number::newNumber(int32_t value) { return newNumber(static_cast(value)); } + +Local Number::newNumber(int64_t value) { return newNumber(static_cast(value)); } + +Local Boolean::newBoolean(bool value) { TEMPLATE_NOT_IMPLEMENTED(); } + +Local Function::newFunction(script::FunctionCallback callback) { + TEMPLATE_NOT_IMPLEMENTED(); +} + +Local Array::newArray(size_t size) { TEMPLATE_NOT_IMPLEMENTED(); } + +Local Array::newArrayImpl(size_t size, const Local* args) { + TEMPLATE_NOT_IMPLEMENTED(); +} + +Local ByteBuffer::newByteBuffer(size_t size) { TEMPLATE_NOT_IMPLEMENTED(); } + +Local ByteBuffer::newByteBuffer(void* nativeBuffer, size_t size) { + TEMPLATE_NOT_IMPLEMENTED(); +} + +Local ByteBuffer::newByteBuffer(std::shared_ptr nativeBuffer, size_t size) { + TEMPLATE_NOT_IMPLEMENTED(); +} + +} // namespace script \ No newline at end of file diff --git a/backend/Python/trait/TraitEngine.h b/backend/Python/trait/TraitEngine.h new file mode 100644 index 00000000..8d6592be --- /dev/null +++ b/backend/Python/trait/TraitEngine.h @@ -0,0 +1,36 @@ +/* + * Tencent is pleased to support the open source community by making ScriptX available. + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * 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. + */ + +#pragma once + +#include +#include "../../src/types.h" + +#define TEMPLATE_NOT_IMPLEMENTED() throw Exception(std::string(__func__) + " not implemented"); + +namespace script { + +namespace py_backend { +class PyEngine; +} + +template <> +struct internal::ImplType { + using type = py_backend::PyEngine; +}; + +} // namespace script \ No newline at end of file diff --git a/backend/Python/trait/TraitException.h b/backend/Python/trait/TraitException.h new file mode 100644 index 00000000..3c5a3be2 --- /dev/null +++ b/backend/Python/trait/TraitException.h @@ -0,0 +1,36 @@ +/* + * Tencent is pleased to support the open source community by making ScriptX available. + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * 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. + */ + +#pragma once + +namespace script { + +namespace py_backend { + +class ExceptionFields { + public: + mutable std::string message_{}; +}; + +} // namespace py_backend + +template <> +struct internal::ImplType { + using type = py_backend::ExceptionFields; +}; + +} // namespace script diff --git a/backend/Python/trait/TraitIncludes.h b/backend/Python/trait/TraitIncludes.h new file mode 100644 index 00000000..67972386 --- /dev/null +++ b/backend/Python/trait/TraitIncludes.h @@ -0,0 +1,26 @@ +/* + * Tencent is pleased to support the open source community by making ScriptX available. + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * 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. + */ + +#pragma once + +#include "../PyEngine.h" +#include "../PyNative.hpp" +#include "../PyReference.hpp" + +// global marco +#define SCRIPTX_BACKEND_TEMPLATE +#define SCRIPTX_LANG_NONE \ No newline at end of file diff --git a/backend/Python/trait/TraitNative.h b/backend/Python/trait/TraitNative.h new file mode 100644 index 00000000..2bb38cd1 --- /dev/null +++ b/backend/Python/trait/TraitNative.h @@ -0,0 +1,46 @@ +/* + * Tencent is pleased to support the open source community by making ScriptX available. + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * 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. + */ + +#pragma once + +namespace script { + +namespace py_backend { + +struct ArgumentsData { + int stackBase; + size_t size; +}; + +struct JscScriptClassState { + ScriptEngine* scriptEngine_ = nullptr; + Weak weakRef_; +}; + +} // namespace py_backend + +template <> +struct internal::ImplType<::script::Arguments> { + using type = py_backend::ArgumentsData; +}; + +template <> +struct internal::ImplType<::script::ScriptClass> { + using type = py_backend::JscScriptClassState; +}; + +} // namespace script \ No newline at end of file diff --git a/backend/Python/trait/TraitReference.h b/backend/Python/trait/TraitReference.h new file mode 100644 index 00000000..b1c7878c --- /dev/null +++ b/backend/Python/trait/TraitReference.h @@ -0,0 +1,41 @@ +/* + * Tencent is pleased to support the open source community by making ScriptX available. + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * 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. + */ + +#pragma once + +#include +#include +#include "../../src/types.h" + +namespace script::internal { + +template +struct ImplType> { + using type = int; +}; + +template +struct ImplType> { + using type = int; +}; + +template +struct ImplType> { + using type = int; +}; + +} // namespace script::internal \ No newline at end of file diff --git a/backend/Python/trait/TraitScope.h b/backend/Python/trait/TraitScope.h new file mode 100644 index 00000000..fbd7dd43 --- /dev/null +++ b/backend/Python/trait/TraitScope.h @@ -0,0 +1,80 @@ +/* + * Tencent is pleased to support the open source community by making ScriptX available. + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * 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. + */ + +#pragma once + +#include "TraitEngine.h" + +namespace script { + +namespace py_backend { + +class EngineScopeImpl { + public: + explicit EngineScopeImpl(PyEngine &) { + // enter engine; + } + + ~EngineScopeImpl() { + // exit engine; + } +}; + +class ExitEngineScopeImpl { + public: + explicit ExitEngineScopeImpl(PyEngine &) { + // exit current entered engine + } + + ~ExitEngineScopeImpl() { + // reenter engine; + } +}; + +class StackFrameScopeImpl { + public: + explicit StackFrameScopeImpl(PyEngine &) { + // enter stack; + } + + ~StackFrameScopeImpl() { + // exit stack; + } + + template + Local returnValue(const Local &localRef) { + return localRef; + } +}; +} // namespace py_backend + +template <> +struct internal::ImplType { + using type = py_backend::EngineScopeImpl; +}; + +template <> +struct internal::ImplType { + using type = py_backend::ExitEngineScopeImpl; +}; + +template <> +struct internal::ImplType { + using type = py_backend::StackFrameScopeImpl; +}; + +} // namespace script \ No newline at end of file diff --git a/backend/Python/trait/TraitUtils.h b/backend/Python/trait/TraitUtils.h new file mode 100644 index 00000000..318334f3 --- /dev/null +++ b/backend/Python/trait/TraitUtils.h @@ -0,0 +1,32 @@ +/* + * Tencent is pleased to support the open source community by making ScriptX available. + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * 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. + */ + +#pragma once + +namespace script { + +template <> +struct internal::ImplType { + using type = int; +}; + +template <> +struct internal::ImplType { + using type = int; +}; + +} // namespace script \ No newline at end of file diff --git a/test/cmake/TestEnv.cmake b/test/cmake/TestEnv.cmake index 1d582f2a..30b91ad5 100644 --- a/test/cmake/TestEnv.cmake +++ b/test/cmake/TestEnv.cmake @@ -159,5 +159,10 @@ elseif (${SCRIPTX_BACKEND} STREQUAL WebAssembly) elseif (${SCRIPTX_BACKEND} STREQUAL QuickJs) include("${SCRIPTX_TEST_LIBS}/quickjs/CMakeLists.txt") set(DEVOPS_LIBS_LIBPATH QuickJs CACHE STRING "" FORCE) +elseif (${SCRIPTX_BACKEND} STREQUAL Python) + set(DEVOPS_LIBS_INCLUDE + "/usr/local/Cellar/python@3.9/3.9.4/Frameworks/Python.framework/Versions/3.9/include/python3.9/" + CACHE STRING "" FORCE) + set(DEVOPS_LIBS_LIBPATH "/usr/local/Cellar/python@3.9/3.9.4/Frameworks/Python.framework/Versions/3.9/lib/libpython3.9.dylib" CACHE STRING "" FORCE) endif () From b05e92c67b876556ab01518bcd92dcdd030361cd Mon Sep 17 00:00:00 2001 From: landerlyoung Date: Sat, 1 May 2021 19:46:35 +0800 Subject: [PATCH 02/10] include --- backend/Python/CMakeLists.txt | 3 +++ backend/Python/PyHelper.cc | 18 ++++++++++++++++++ backend/Python/PyHelper.h | 31 +++++++++++++++++++++++++++++++ backend/Python/PyHelper.hpp | 21 +++++++++++++++++++++ 4 files changed, 73 insertions(+) create mode 100644 backend/Python/PyHelper.cc create mode 100644 backend/Python/PyHelper.h create mode 100644 backend/Python/PyHelper.hpp diff --git a/backend/Python/CMakeLists.txt b/backend/Python/CMakeLists.txt index d547ca57..047a7387 100644 --- a/backend/Python/CMakeLists.txt +++ b/backend/Python/CMakeLists.txt @@ -2,6 +2,9 @@ target_sources(ScriptX PRIVATE ${CMAKE_CURRENT_LIST_DIR}/PyEngine.cc ${CMAKE_CURRENT_LIST_DIR}/PyEngine.h ${CMAKE_CURRENT_LIST_DIR}/PyException.cc + ${CMAKE_CURRENT_LIST_DIR}/PyHelper.h + ${CMAKE_CURRENT_LIST_DIR}/PyHelper.hpp + ${CMAKE_CURRENT_LIST_DIR}/PyHelper.cc ${CMAKE_CURRENT_LIST_DIR}/PyLocalReference.cc ${CMAKE_CURRENT_LIST_DIR}/PyNative.cc ${CMAKE_CURRENT_LIST_DIR}/PyNative.hpp diff --git a/backend/Python/PyHelper.cc b/backend/Python/PyHelper.cc new file mode 100644 index 00000000..5db6fa6b --- /dev/null +++ b/backend/Python/PyHelper.cc @@ -0,0 +1,18 @@ +/* + * Tencent is pleased to support the open source community by making ScriptX available. + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * 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 "PyHelper.hpp" \ No newline at end of file diff --git a/backend/Python/PyHelper.h b/backend/Python/PyHelper.h new file mode 100644 index 00000000..0cc41816 --- /dev/null +++ b/backend/Python/PyHelper.h @@ -0,0 +1,31 @@ +/* + * Tencent is pleased to support the open source community by making ScriptX available. + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * 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. + */ + +#pragma once + +#include "../../src/foundation.h" + +// docs: https://docs.python.org/3/c-api/index.html + +SCRIPTX_BEGIN_INCLUDE_LIBRARY +#ifndef PY_SSIZE_T_CLEAN +#define PY_SSIZE_T_CLEAN +#endif +#include +SCRIPTX_END_INCLUDE_LIBRARY + +namespace script::py_backend {} diff --git a/backend/Python/PyHelper.hpp b/backend/Python/PyHelper.hpp new file mode 100644 index 00000000..32a1058d --- /dev/null +++ b/backend/Python/PyHelper.hpp @@ -0,0 +1,21 @@ +/* + * Tencent is pleased to support the open source community by making ScriptX available. + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * 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. + */ + +#pragma once +#include "PyHelper.h" + +namespace script::py_backend {} \ No newline at end of file From 7d6247c9e4d9ea41cc2c30c738701156af690826 Mon Sep 17 00:00:00 2001 From: landerlyoung Date: Tue, 21 Sep 2021 21:50:43 +0800 Subject: [PATCH 03/10] wip --- backend/Python/PyEngine.cc | 10 +++--- backend/Python/PyEngine.h | 1 + backend/Python/PyHelper.cc | 27 +++++++++++++++- backend/Python/PyHelper.h | 13 ++++++-- backend/Python/PyHelper.hpp | 12 ++++++- backend/Python/PyValue.cc | 46 +++++++++++++++++++++------ backend/Python/trait/TraitException.h | 2 ++ backend/Python/trait/TraitIncludes.h | 4 +-- backend/Python/trait/TraitNative.h | 1 + backend/Python/trait/TraitReference.h | 7 ++-- backend/Python/trait/TraitUtils.h | 7 +++- test/src/gtest_main.cc | 5 ++- 12 files changed, 111 insertions(+), 24 deletions(-) diff --git a/backend/Python/PyEngine.cc b/backend/Python/PyEngine.cc index 157cd0e8..94c2b8a9 100644 --- a/backend/Python/PyEngine.cc +++ b/backend/Python/PyEngine.cc @@ -17,16 +17,18 @@ #include "PyEngine.h" #include "../../src/Utils.h" +#include "pydebug.h" +#include "pylifecycle.h" namespace script::py_backend { -PyEngine::PyEngine(std::shared_ptr queue) {} +PyEngine::PyEngine(std::shared_ptr queue) { Py_Initialize(); } PyEngine::PyEngine() : PyEngine(std::shared_ptr{}) {} PyEngine::~PyEngine() = default; -void PyEngine::destroy() noexcept {} +void PyEngine::destroy() noexcept { ScriptEngine::destroyUserData(); } Local PyEngine::get(const Local& key) { return Local(); } @@ -50,9 +52,9 @@ void PyEngine::gc() {} void PyEngine::adjustAssociatedMemory(int64_t count) {} -ScriptLanguage PyEngine::getLanguageType() { return ScriptLanguage::kJavaScript; } +ScriptLanguage PyEngine::getLanguageType() { return ScriptLanguage::kPython; } -std::string PyEngine::getEngineVersion() { return ""; } +std::string PyEngine::getEngineVersion() { return Py_GetVersion(); } bool PyEngine::isDestroying() const { return false; } diff --git a/backend/Python/PyEngine.h b/backend/Python/PyEngine.h index 21df182d..9417415b 100644 --- a/backend/Python/PyEngine.h +++ b/backend/Python/PyEngine.h @@ -20,6 +20,7 @@ #include "../../src/Engine.h" #include "../../src/Exception.h" #include "../../src/utils/MessageQueue.h" +#include "PyHelper.h" namespace script::py_backend { diff --git a/backend/Python/PyHelper.cc b/backend/Python/PyHelper.cc index 5db6fa6b..d02f4b0e 100644 --- a/backend/Python/PyHelper.cc +++ b/backend/Python/PyHelper.cc @@ -15,4 +15,29 @@ * limitations under the License. */ -#include "PyHelper.hpp" \ No newline at end of file +#include "PyHelper.hpp" + +namespace script::py_backend { + +PyObject* checkException(PyObject* obj) { + if (!obj) { + checkException(); + } + return obj; +} + +int checkException(int ret) { + if (ret == -1) { + checkException(); + } + return ret; +} + +void checkException() { + auto err = PyErr_Occurred(); + if (err) { + // TODO + } +} + +} // namespace script::py_backend \ No newline at end of file diff --git a/backend/Python/PyHelper.h b/backend/Python/PyHelper.h index 0cc41816..4bfa85a6 100644 --- a/backend/Python/PyHelper.h +++ b/backend/Python/PyHelper.h @@ -19,7 +19,10 @@ #include "../../src/foundation.h" -// docs: https://docs.python.org/3/c-api/index.html +// docs: +// https://docs.python.org/3/c-api/index.html +// https://docs.python.org/3/extending/embedding.html +// https://docs.python.org/2/c-api/init.html#thread-state-and-the-global-interpreter-lock SCRIPTX_BEGIN_INCLUDE_LIBRARY #ifndef PY_SSIZE_T_CLEAN @@ -28,4 +31,10 @@ SCRIPTX_BEGIN_INCLUDE_LIBRARY #include SCRIPTX_END_INCLUDE_LIBRARY -namespace script::py_backend {} +namespace script::py_backend { + +PyObject* checkException(PyObject* obj); +int checkException(int ret); +void checkException(); + +} // namespace script::py_backend diff --git a/backend/Python/PyHelper.hpp b/backend/Python/PyHelper.hpp index 32a1058d..503fa184 100644 --- a/backend/Python/PyHelper.hpp +++ b/backend/Python/PyHelper.hpp @@ -16,6 +16,16 @@ */ #pragma once +#include "../../src/Reference.h" #include "PyHelper.h" -namespace script::py_backend {} \ No newline at end of file +namespace script::py_backend { + +struct py_interop { + template + static Local makeLocal(PyObject* ref) { + return Local(ref); + } +}; + +} // namespace script::py_backend \ No newline at end of file diff --git a/backend/Python/PyValue.cc b/backend/Python/PyValue.cc index 548ad154..f06ab80b 100644 --- a/backend/Python/PyValue.cc +++ b/backend/Python/PyValue.cc @@ -19,21 +19,39 @@ #include "../../src/Reference.h" #include "../../src/Scope.h" #include "../../src/Value.h" +#include "PyHelper.hpp" + +using script::py_backend::checkException; +using script::py_backend::py_interop; namespace script { -Local Object::newObject() { TEMPLATE_NOT_IMPLEMENTED(); } +template +Local checkAndMakeLocal(PyObject* ref) { + return py_interop::makeLocal(checkException(ref)); +} + +// for python this creates an empty dict +Local Object::newObject() { return checkAndMakeLocal(PyDict_New()); } Local Object::newObjectImpl(const Local& type, size_t size, const Local* args) { TEMPLATE_NOT_IMPLEMENTED(); } -Local String::newString(const char* utf8) { TEMPLATE_NOT_IMPLEMENTED(); } +Local String::newString(const char* utf8) { + return checkAndMakeLocal(PyBytes_FromString(utf8)); +} -Local String::newString(std::string_view utf8) { TEMPLATE_NOT_IMPLEMENTED(); } +Local String::newString(std::string_view utf8) { + return checkAndMakeLocal( + PyBytes_FromStringAndSize(utf8.data(), static_cast(utf8.length()))); +} -Local String::newString(const std::string& utf8) { TEMPLATE_NOT_IMPLEMENTED(); } +Local String::newString(const std::string& utf8) { + return checkAndMakeLocal( + PyBytes_FromStringAndSize(utf8.c_str(), static_cast(utf8.length()))); +} #if defined(__cpp_char8_t) @@ -51,19 +69,29 @@ Local String::newString(const std::u8string& utf8) { return newString(ut Local Number::newNumber(float value) { return newNumber(static_cast(value)); } -Local Number::newNumber(double value) { TEMPLATE_NOT_IMPLEMENTED(); } +Local Number::newNumber(double value) { + return checkAndMakeLocal(PyLong_FromDouble(value)); +} -Local Number::newNumber(int32_t value) { return newNumber(static_cast(value)); } +Local Number::newNumber(int32_t value) { + return checkAndMakeLocal(PyLong_FromLong(static_cast(value))); +} -Local Number::newNumber(int64_t value) { return newNumber(static_cast(value)); } +Local Number::newNumber(int64_t value) { + return checkAndMakeLocal(PyLong_FromLongLong(static_cast(value))); +} -Local Boolean::newBoolean(bool value) { TEMPLATE_NOT_IMPLEMENTED(); } +Local Boolean::newBoolean(bool value) { + return checkAndMakeLocal(PyBool_FromLong(value)); +} Local Function::newFunction(script::FunctionCallback callback) { TEMPLATE_NOT_IMPLEMENTED(); } -Local Array::newArray(size_t size) { TEMPLATE_NOT_IMPLEMENTED(); } +Local Array::newArray(size_t size) { + return checkAndMakeLocal(PyList_New(static_cast(size))); +} Local Array::newArrayImpl(size_t size, const Local* args) { TEMPLATE_NOT_IMPLEMENTED(); diff --git a/backend/Python/trait/TraitException.h b/backend/Python/trait/TraitException.h index 3c5a3be2..80b13f49 100644 --- a/backend/Python/trait/TraitException.h +++ b/backend/Python/trait/TraitException.h @@ -16,6 +16,8 @@ */ #pragma once +#include +#include "../../src/types.h" namespace script { diff --git a/backend/Python/trait/TraitIncludes.h b/backend/Python/trait/TraitIncludes.h index 67972386..6f22d372 100644 --- a/backend/Python/trait/TraitIncludes.h +++ b/backend/Python/trait/TraitIncludes.h @@ -22,5 +22,5 @@ #include "../PyReference.hpp" // global marco -#define SCRIPTX_BACKEND_TEMPLATE -#define SCRIPTX_LANG_NONE \ No newline at end of file +#define SCRIPTX_BACKEND_PYTHON +#define SCRIPTX_LANG_PYTHON \ No newline at end of file diff --git a/backend/Python/trait/TraitNative.h b/backend/Python/trait/TraitNative.h index 2bb38cd1..4f9774a5 100644 --- a/backend/Python/trait/TraitNative.h +++ b/backend/Python/trait/TraitNative.h @@ -16,6 +16,7 @@ */ #pragma once +#include "../../src/types.h" namespace script { diff --git a/backend/Python/trait/TraitReference.h b/backend/Python/trait/TraitReference.h index b1c7878c..d45c7ed5 100644 --- a/backend/Python/trait/TraitReference.h +++ b/backend/Python/trait/TraitReference.h @@ -20,22 +20,23 @@ #include #include #include "../../src/types.h" +#include "../PyHelper.h" namespace script::internal { template struct ImplType> { - using type = int; + using type = PyObject*; }; template struct ImplType> { - using type = int; + using type = PyObject*; }; template struct ImplType> { - using type = int; + using type = PyObject*; }; } // namespace script::internal \ No newline at end of file diff --git a/backend/Python/trait/TraitUtils.h b/backend/Python/trait/TraitUtils.h index 318334f3..506a9c46 100644 --- a/backend/Python/trait/TraitUtils.h +++ b/backend/Python/trait/TraitUtils.h @@ -16,9 +16,14 @@ */ #pragma once +#include "../../src/types.h" namespace script { +namespace py_backend { +struct py_interop; +} + template <> struct internal::ImplType { using type = int; @@ -26,7 +31,7 @@ struct internal::ImplType { template <> struct internal::ImplType { - using type = int; + using type = py_backend::py_interop; }; } // namespace script \ No newline at end of file diff --git a/test/src/gtest_main.cc b/test/src/gtest_main.cc index e9a0258b..ab9c6c5d 100644 --- a/test/src/gtest_main.cc +++ b/test/src/gtest_main.cc @@ -65,13 +65,16 @@ void ScriptXTestFixture::SetUp() { using script::String; EngineScope engineScope(engine); +#if defined(SCRIPTX_LANG_JAVASCRIPT) && !defined(SCRIPTX_BACKEND_WEBASSEMBLY) auto log = Function::newFunction(consoleLog); -#ifndef SCRIPTX_BACKEND_WEBASSEMBLY auto console = Object::newObject(); console.set(String::newString(u8"log"), log); engine->set(String::newString(u8"console"), console); #endif +#ifdef SCRIPTX_BACKEND_LUA + auto log = Function::newFunction(consoleLog); engine->set(String::newString(u8"print"), log); +#endif } } From 358e056cee73ee7abbdb56e24f76897e6879ff1a Mon Sep 17 00:00:00 2001 From: landerlyoung Date: Sun, 12 Dec 2021 20:54:32 +0800 Subject: [PATCH 04/10] update --- backend/Python/PyEngine.cc | 13 ++++++------- backend/Python/PyEngine.h | 4 +++- backend/Python/PyHelper.h | 1 + backend/Python/trait/TraitNative.h | 4 ++-- backend/Python/trait/TraitScope.h | 2 +- backend/Template/trait/TraitNative.h | 4 ++-- test/cmake/TestEnv.cmake | 4 ++-- 7 files changed, 17 insertions(+), 15 deletions(-) diff --git a/backend/Python/PyEngine.cc b/backend/Python/PyEngine.cc index 94c2b8a9..2d3c2dea 100644 --- a/backend/Python/PyEngine.cc +++ b/backend/Python/PyEngine.cc @@ -17,14 +17,15 @@ #include "PyEngine.h" #include "../../src/Utils.h" -#include "pydebug.h" -#include "pylifecycle.h" namespace script::py_backend { -PyEngine::PyEngine(std::shared_ptr queue) { Py_Initialize(); } +PyEngine::PyEngine(std::shared_ptr queue) + : queue_(queue ? std::move(queue) : std::make_shared()) { + Py_Initialize(); +} -PyEngine::PyEngine() : PyEngine(std::shared_ptr{}) {} +PyEngine::PyEngine() : PyEngine(nullptr) {} PyEngine::~PyEngine() = default; @@ -44,9 +45,7 @@ Local PyEngine::eval(const Local& script, const Local& sou return Local(); } -std::shared_ptr PyEngine::messageQueue() { - return std::shared_ptr(); -} +std::shared_ptr PyEngine::messageQueue() { return queue_; } void PyEngine::gc() {} diff --git a/backend/Python/PyEngine.h b/backend/Python/PyEngine.h index 9417415b..e1c57249 100644 --- a/backend/Python/PyEngine.h +++ b/backend/Python/PyEngine.h @@ -25,7 +25,9 @@ namespace script::py_backend { class PyEngine : public ScriptEngine { - protected: + private: + std::shared_ptr<::script::utils::MessageQueue> queue_; + public: PyEngine(std::shared_ptr<::script::utils::MessageQueue> queue); diff --git a/backend/Python/PyHelper.h b/backend/Python/PyHelper.h index 4bfa85a6..81135d6d 100644 --- a/backend/Python/PyHelper.h +++ b/backend/Python/PyHelper.h @@ -29,6 +29,7 @@ SCRIPTX_BEGIN_INCLUDE_LIBRARY #define PY_SSIZE_T_CLEAN #endif #include +#include SCRIPTX_END_INCLUDE_LIBRARY namespace script::py_backend { diff --git a/backend/Python/trait/TraitNative.h b/backend/Python/trait/TraitNative.h index 4f9774a5..79f57f3e 100644 --- a/backend/Python/trait/TraitNative.h +++ b/backend/Python/trait/TraitNative.h @@ -27,7 +27,7 @@ struct ArgumentsData { size_t size; }; -struct JscScriptClassState { +struct ScriptClassState { ScriptEngine* scriptEngine_ = nullptr; Weak weakRef_; }; @@ -41,7 +41,7 @@ struct internal::ImplType<::script::Arguments> { template <> struct internal::ImplType<::script::ScriptClass> { - using type = py_backend::JscScriptClassState; + using type = py_backend::ScriptClassState; }; } // namespace script \ No newline at end of file diff --git a/backend/Python/trait/TraitScope.h b/backend/Python/trait/TraitScope.h index fbd7dd43..f0162ef5 100644 --- a/backend/Python/trait/TraitScope.h +++ b/backend/Python/trait/TraitScope.h @@ -25,7 +25,7 @@ namespace py_backend { class EngineScopeImpl { public: - explicit EngineScopeImpl(PyEngine &) { + explicit EngineScopeImpl(PyEngine &, PyEngine *) { // enter engine; } diff --git a/backend/Template/trait/TraitNative.h b/backend/Template/trait/TraitNative.h index 568325b6..3077e648 100644 --- a/backend/Template/trait/TraitNative.h +++ b/backend/Template/trait/TraitNative.h @@ -26,7 +26,7 @@ struct ArgumentsData { size_t size; }; -struct JscScriptClassState { +struct ScriptClassState { ScriptEngine* scriptEngine_ = nullptr; Weak weakRef_; }; @@ -40,7 +40,7 @@ struct internal::ImplType<::script::Arguments> { template <> struct internal::ImplType<::script::ScriptClass> { - using type = template_backend::JscScriptClassState; + using type = template_backend::ScriptClassState; }; } // namespace script \ No newline at end of file diff --git a/test/cmake/TestEnv.cmake b/test/cmake/TestEnv.cmake index 30b91ad5..f04e0735 100644 --- a/test/cmake/TestEnv.cmake +++ b/test/cmake/TestEnv.cmake @@ -161,8 +161,8 @@ elseif (${SCRIPTX_BACKEND} STREQUAL QuickJs) set(DEVOPS_LIBS_LIBPATH QuickJs CACHE STRING "" FORCE) elseif (${SCRIPTX_BACKEND} STREQUAL Python) set(DEVOPS_LIBS_INCLUDE - "/usr/local/Cellar/python@3.9/3.9.4/Frameworks/Python.framework/Versions/3.9/include/python3.9/" + "/usr/local/Cellar/python@3.10/3.10.0_2/Frameworks/Python.framework/Headers/" CACHE STRING "" FORCE) - set(DEVOPS_LIBS_LIBPATH "/usr/local/Cellar/python@3.9/3.9.4/Frameworks/Python.framework/Versions/3.9/lib/libpython3.9.dylib" CACHE STRING "" FORCE) + set(DEVOPS_LIBS_LIBPATH "/usr/local/Cellar/python@3.10/3.10.0_2/Frameworks/Python.framework/Versions/Current/lib/libpython3.10.dylib" CACHE STRING "" FORCE) endif () From c1adeb673490dbaf3a4e77a248595bad7cb1051c Mon Sep 17 00:00:00 2001 From: landerlyoung Date: Sat, 18 Dec 2021 15:41:49 +0800 Subject: [PATCH 05/10] x --- backend/JavaScriptCore/JscUtils.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/JavaScriptCore/JscUtils.cc b/backend/JavaScriptCore/JscUtils.cc index bfa30a8c..f2fc0e78 100644 --- a/backend/JavaScriptCore/JscUtils.cc +++ b/backend/JavaScriptCore/JscUtils.cc @@ -73,7 +73,7 @@ std::string_view StringHolder::stringView() const { std::string StringHolder::string() const { jsc_backend::initString(internalHolder_); internalHolder_.inited = false; - return std::move(internalHolder_.stringContent); + return internalHolder_.stringContent; } #if defined(__cpp_char8_t) From 0014762e3388963cad841f9439f146445ecf0026 Mon Sep 17 00:00:00 2001 From: landerlyoung Date: Sat, 18 Dec 2021 22:08:52 +0800 Subject: [PATCH 06/10] py local ref-count --- backend/Python/PyHelper.h | 2 +- backend/Python/PyLocalReference.cc | 60 ++++++++++++++++++++---------- backend/Python/PyReference.hpp | 8 ++++ 3 files changed, 49 insertions(+), 21 deletions(-) diff --git a/backend/Python/PyHelper.h b/backend/Python/PyHelper.h index 81135d6d..db8375e1 100644 --- a/backend/Python/PyHelper.h +++ b/backend/Python/PyHelper.h @@ -22,7 +22,7 @@ // docs: // https://docs.python.org/3/c-api/index.html // https://docs.python.org/3/extending/embedding.html -// https://docs.python.org/2/c-api/init.html#thread-state-and-the-global-interpreter-lock +// https://docs.python.org/3.8/c-api/init.html#thread-state-and-the-global-interpreter-lock SCRIPTX_BEGIN_INCLUDE_LIBRARY #ifndef PY_SSIZE_T_CLEAN diff --git a/backend/Python/PyLocalReference.cc b/backend/Python/PyLocalReference.cc index d4f202d8..3db91ca5 100644 --- a/backend/Python/PyLocalReference.cc +++ b/backend/Python/PyLocalReference.cc @@ -15,22 +15,37 @@ * limitations under the License. */ -#include +#include "../../src/Native.hpp" +#include "../../src/Reference.h" +#include "../../src/Utils.h" +#include "../../src/Value.h" +#include "PyReference.hpp" namespace script { -#define REF_IMPL_BASIC_FUNC(ValueType) \ - Local::Local(const Local& copy) : val_(copy.val_) {} \ - Local::Local(Local&& move) noexcept : val_(move.val_) {} \ - Local::~Local() {} \ - Local& Local::operator=(const Local& from) { \ - Local(from).swap(*this); \ - return *this; \ - } \ - Local& Local::operator=(Local&& move) noexcept { \ - Local(std::move(move)).swap(*this); \ - return *this; \ - } \ +namespace py_backend { +void valueConstructorCheck(PyObject* value) { + SCRIPTX_UNUSED(value); +#ifndef NDEBUG + if (!value) throw Exception("null reference"); +#endif +} +} // namespace py_backend + +#define REF_IMPL_BASIC_FUNC(ValueType) \ + Local::Local(const Local& copy) : val_(py_backend::incRef(copy.val_)) {} \ + Local::Local(Local&& move) noexcept : val_(move.val_) { \ + move.val_ = nullptr; \ + } \ + Local::~Local() { py_backend::decRef(val_); } \ + Local& Local::operator=(const Local& from) { \ + Local(from).swap(*this); \ + return *this; \ + } \ + Local& Local::operator=(Local&& move) noexcept { \ + Local(std::move(move)).swap(*this); \ + return *this; \ + } \ void Local::swap(Local& rhs) noexcept { std::swap(val_, rhs.val_); } #define REF_IMPL_BASIC_EQUALS(ValueType) \ @@ -39,12 +54,14 @@ namespace script { } #define REF_IMPL_BASIC_NOT_VALUE(ValueType) \ - Local::Local(InternalLocalRef val) : val_(val) {} \ + Local::Local(InternalLocalRef val) : val_(val) { \ + py_backend::valueConstructorCheck(val); \ + } \ Local Local::describe() const { return asValue().describe(); } \ std::string Local::describeUtf8() const { return asValue().describeUtf8(); } #define REF_IMPL_TO_VALUE(ValueType) \ - Local Local::asValue() const { return Local(/*TMPL: value*/); } + Local Local::asValue() const { return Local(val_); } REF_IMPL_BASIC_FUNC(Value) @@ -92,11 +109,14 @@ REF_IMPL_TO_VALUE(Unsupported) Local::Local() noexcept : val_() {} -Local::Local(InternalLocalRef v8Local) : val_(v8Local) {} +Local::Local(InternalLocalRef ref) : val_(ref) {} -bool Local::isNull() const { return false; } +bool Local::isNull() const { return val_ == nullptr; } -void Local::reset() {} +void Local::reset() { + py_backend::decRef(val_); + val_ = nullptr; +} ValueKind Local::getKind() const { if (isNull()) { @@ -122,9 +142,9 @@ ValueKind Local::getKind() const { bool Local::isString() const { return false; } -bool Local::isNumber() const { return false; } +bool Local::isNumber() const { return PyNumber_Check(val_); } -bool Local::isBoolean() const { return false; } +bool Local::isBoolean() const { return PyBool_Check(val_); } bool Local::isFunction() const { return false; } diff --git a/backend/Python/PyReference.hpp b/backend/Python/PyReference.hpp index 4c7fdb5d..393788a4 100644 --- a/backend/Python/PyReference.hpp +++ b/backend/Python/PyReference.hpp @@ -20,6 +20,14 @@ namespace script { +namespace py_backend { + +inline PyObject* incRef(PyObject* ref) { return Py_XNewRef(ref); } + +inline void decRef(PyObject* ref) { Py_XDECREF(ref); } + +} // namespace py_backend + template Global::Global() noexcept : val_() {} From 5c4f3b4d6446990c66457d38c44a6580aff55bcb Mon Sep 17 00:00:00 2001 From: landerlyoung Date: Sat, 18 Dec 2021 23:29:14 +0800 Subject: [PATCH 07/10] py scope --- backend/Python/CMakeLists.txt | 2 ++ backend/Python/PyScope.cc | 31 ++++++++++++++++++ backend/Python/PyScope.h | 53 +++++++++++++++++++++++++++++++ backend/Python/trait/TraitScope.h | 42 +----------------------- 4 files changed, 87 insertions(+), 41 deletions(-) create mode 100644 backend/Python/PyScope.cc create mode 100644 backend/Python/PyScope.h diff --git a/backend/Python/CMakeLists.txt b/backend/Python/CMakeLists.txt index 047a7387..161c361c 100644 --- a/backend/Python/CMakeLists.txt +++ b/backend/Python/CMakeLists.txt @@ -9,6 +9,8 @@ target_sources(ScriptX PRIVATE ${CMAKE_CURRENT_LIST_DIR}/PyNative.cc ${CMAKE_CURRENT_LIST_DIR}/PyNative.hpp ${CMAKE_CURRENT_LIST_DIR}/PyReference.hpp + ${CMAKE_CURRENT_LIST_DIR}/PyScope.h + ${CMAKE_CURRENT_LIST_DIR}/PyScope.cc ${CMAKE_CURRENT_LIST_DIR}/PyUtils.cc ${CMAKE_CURRENT_LIST_DIR}/PyValue.cc ) \ No newline at end of file diff --git a/backend/Python/PyScope.cc b/backend/Python/PyScope.cc new file mode 100644 index 00000000..d34be031 --- /dev/null +++ b/backend/Python/PyScope.cc @@ -0,0 +1,31 @@ +/* + * Tencent is pleased to support the open source community by making ScriptX available. + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * 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 "PyScope.h" + +// reference +// https://docs.python.org/3.8/c-api/init.html#thread-state-and-the-global-interpreter-lock + +namespace script::py_backend { + +EngineScopeImpl::EngineScopeImpl(PyEngine &, PyEngine *) { gilState_ = PyGILState_Ensure(); } +EngineScopeImpl::~EngineScopeImpl() { PyGILState_Release(gilState_); } + +ExitEngineScopeImpl::ExitEngineScopeImpl(PyEngine &) { threadState = PyEval_SaveThread(); } +ExitEngineScopeImpl::~ExitEngineScopeImpl() { PyEval_RestoreThread(threadState); } + +} // namespace script::py_backend \ No newline at end of file diff --git a/backend/Python/PyScope.h b/backend/Python/PyScope.h new file mode 100644 index 00000000..8be5e839 --- /dev/null +++ b/backend/Python/PyScope.h @@ -0,0 +1,53 @@ +/* + * Tencent is pleased to support the open source community by making ScriptX available. + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * 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. + */ + +#pragma once +#include "../../src/Reference.h" +#include "PyHelper.h" + +namespace script::py_backend { + +class PyEngine; + +class EngineScopeImpl { + PyGILState_STATE gilState_ = PyGILState_UNLOCKED; + + public: + explicit EngineScopeImpl(PyEngine &, PyEngine *); + + ~EngineScopeImpl(); +}; + +class ExitEngineScopeImpl { + PyThreadState *threadState = nullptr; + + public: + explicit ExitEngineScopeImpl(PyEngine &); + + ~ExitEngineScopeImpl(); +}; + +class StackFrameScopeImpl { + public: + explicit StackFrameScopeImpl(PyEngine &) {} + + template + Local returnValue(const Local &localRef) { + return localRef; + } +}; +} // namespace script::py_backend diff --git a/backend/Python/trait/TraitScope.h b/backend/Python/trait/TraitScope.h index f0162ef5..d3cd8996 100644 --- a/backend/Python/trait/TraitScope.h +++ b/backend/Python/trait/TraitScope.h @@ -17,51 +17,11 @@ #pragma once +#include "../PyScope.h" #include "TraitEngine.h" namespace script { -namespace py_backend { - -class EngineScopeImpl { - public: - explicit EngineScopeImpl(PyEngine &, PyEngine *) { - // enter engine; - } - - ~EngineScopeImpl() { - // exit engine; - } -}; - -class ExitEngineScopeImpl { - public: - explicit ExitEngineScopeImpl(PyEngine &) { - // exit current entered engine - } - - ~ExitEngineScopeImpl() { - // reenter engine; - } -}; - -class StackFrameScopeImpl { - public: - explicit StackFrameScopeImpl(PyEngine &) { - // enter stack; - } - - ~StackFrameScopeImpl() { - // exit stack; - } - - template - Local returnValue(const Local &localRef) { - return localRef; - } -}; -} // namespace py_backend - template <> struct internal::ImplType { using type = py_backend::EngineScopeImpl; From b5d09f9805d9f4e99264dda2ba8751d888cbfa31 Mon Sep 17 00:00:00 2001 From: landerlyoung Date: Sun, 19 Dec 2021 15:21:31 +0800 Subject: [PATCH 08/10] py equals --- backend/Python/PyLocalReference.cc | 13 ++++++++++++- backend/Python/PyScope.cc | 4 ++-- backend/Python/PyScope.h | 4 ++-- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/backend/Python/PyLocalReference.cc b/backend/Python/PyLocalReference.cc index 3db91ca5..d2ee9a6c 100644 --- a/backend/Python/PyLocalReference.cc +++ b/backend/Python/PyLocalReference.cc @@ -178,7 +178,18 @@ Local Local::asUnsupported() const { throw Exception("can't cast value as Unsupported"); } -bool Local::operator==(const script::Local& other) const { return false; } +bool Local::operator==(const script::Local& other) const { + // TODO: nullptr vs None + auto lhs = val_; + auto rhs = other.val_; + + // nullptr == nullptr + if (lhs == nullptr || rhs == nullptr) { + return lhs == rhs; + } + + return PyObject_RichCompareBool(lhs, rhs, Py_EQ); +} Local Local::describe() const { TEMPLATE_NOT_IMPLEMENTED(); } diff --git a/backend/Python/PyScope.cc b/backend/Python/PyScope.cc index d34be031..fd45effb 100644 --- a/backend/Python/PyScope.cc +++ b/backend/Python/PyScope.cc @@ -22,10 +22,10 @@ namespace script::py_backend { -EngineScopeImpl::EngineScopeImpl(PyEngine &, PyEngine *) { gilState_ = PyGILState_Ensure(); } +EngineScopeImpl::EngineScopeImpl(PyEngine &, PyEngine *) : gilState_(PyGILState_Ensure()) {} EngineScopeImpl::~EngineScopeImpl() { PyGILState_Release(gilState_); } -ExitEngineScopeImpl::ExitEngineScopeImpl(PyEngine &) { threadState = PyEval_SaveThread(); } +ExitEngineScopeImpl::ExitEngineScopeImpl(PyEngine &) : threadState(PyEval_SaveThread()) {} ExitEngineScopeImpl::~ExitEngineScopeImpl() { PyEval_RestoreThread(threadState); } } // namespace script::py_backend \ No newline at end of file diff --git a/backend/Python/PyScope.h b/backend/Python/PyScope.h index 8be5e839..0d6f98ae 100644 --- a/backend/Python/PyScope.h +++ b/backend/Python/PyScope.h @@ -24,7 +24,7 @@ namespace script::py_backend { class PyEngine; class EngineScopeImpl { - PyGILState_STATE gilState_ = PyGILState_UNLOCKED; + PyGILState_STATE gilState_; public: explicit EngineScopeImpl(PyEngine &, PyEngine *); @@ -33,7 +33,7 @@ class EngineScopeImpl { }; class ExitEngineScopeImpl { - PyThreadState *threadState = nullptr; + PyThreadState *threadState; public: explicit ExitEngineScopeImpl(PyEngine &); From 076cfdd09a981699f8efdb4f4cb109f7e7f9b4f4 Mon Sep 17 00:00:00 2001 From: landerlyoung Date: Sun, 19 Dec 2021 16:50:07 +0800 Subject: [PATCH 09/10] py arguments & newFunction --- backend/Python/PyHelper.h | 2 ++ backend/Python/PyHelper.hpp | 27 +++++++++++++++-- backend/Python/PyNative.cc | 26 ++++++++++++---- backend/Python/PyValue.cc | 48 +++++++++++++++++++++++++++++- backend/Python/trait/TraitNative.h | 6 ++-- backend/Python/trait/TraitUtils.h | 4 +-- 6 files changed, 98 insertions(+), 15 deletions(-) diff --git a/backend/Python/PyHelper.h b/backend/Python/PyHelper.h index db8375e1..8158e146 100644 --- a/backend/Python/PyHelper.h +++ b/backend/Python/PyHelper.h @@ -34,6 +34,8 @@ SCRIPTX_END_INCLUDE_LIBRARY namespace script::py_backend { +class PyEngine; + PyObject* checkException(PyObject* obj); int checkException(int ret); void checkException(); diff --git a/backend/Python/PyHelper.hpp b/backend/Python/PyHelper.hpp index 503fa184..2197ed34 100644 --- a/backend/Python/PyHelper.hpp +++ b/backend/Python/PyHelper.hpp @@ -16,16 +16,37 @@ */ #pragma once +#include "../../src/Native.hpp" #include "../../src/Reference.h" #include "PyHelper.h" -namespace script::py_backend { +namespace script { struct py_interop { - template + template static Local makeLocal(PyObject* ref) { return Local(ref); } + + /** + * @return stolen ref. + */ + template + static PyObject* toPy(const Local& ref) { + return Py_XNewRef(ref.val_); + } + + /** + * @return borrowed ref. + */ + template + static PyObject* asPy(const Local& ref) { + return ref.val_; + } + + static Arguments makeArguments(py_backend::PyEngine* engine, PyObject* self, PyObject* args) { + return Arguments(py_backend::ArgumentsData{engine, self, args}); + } }; -} // namespace script::py_backend \ No newline at end of file +} // namespace script \ No newline at end of file diff --git a/backend/Python/PyNative.cc b/backend/Python/PyNative.cc index 05c10e6a..703a43c0 100644 --- a/backend/Python/PyNative.cc +++ b/backend/Python/PyNative.cc @@ -15,7 +15,9 @@ * limitations under the License. */ -#include +#include "../../src/Native.hpp" +#include "PyEngine.h" +#include "PyHelper.hpp" namespace script { @@ -23,15 +25,27 @@ Arguments::Arguments(InternalCallbackInfoType callbackInfo) : callbackInfo_(call Arguments::~Arguments() = default; -Local Arguments::thiz() const { TEMPLATE_NOT_IMPLEMENTED(); } +Local Arguments::thiz() const { + return py_interop::makeLocal(callbackInfo_.self).asObject(); +} -bool Arguments::hasThiz() const { TEMPLATE_NOT_IMPLEMENTED(); } +bool Arguments::hasThiz() const { return callbackInfo_.self != nullptr; } -size_t Arguments::size() const { TEMPLATE_NOT_IMPLEMENTED(); } +size_t Arguments::size() const { + if (!callbackInfo_.args) { + return 0; + } + return PyTuple_Size(callbackInfo_.args); +} -Local Arguments::operator[](size_t i) const { return {}; } +Local Arguments::operator[](size_t i) const { + if (i < size()) { + return py_interop::makeLocal(PyTuple_GetItem(callbackInfo_.args, i)); + } + return {}; +} -ScriptEngine* Arguments::engine() const { return nullptr; } +ScriptEngine* Arguments::engine() const { return callbackInfo_.engine; } ScriptClass::ScriptClass(const script::Local& scriptObject) : internalState_() { TEMPLATE_NOT_IMPLEMENTED(); diff --git a/backend/Python/PyValue.cc b/backend/Python/PyValue.cc index f06ab80b..11ba4e8f 100644 --- a/backend/Python/PyValue.cc +++ b/backend/Python/PyValue.cc @@ -85,8 +85,54 @@ Local Boolean::newBoolean(bool value) { return checkAndMakeLocal(PyBool_FromLong(value)); } +namespace { + +static constexpr const char* kFunctionDataName = "capsule_function_data"; + +struct FunctionData { + FunctionCallback function; + py_backend::PyEngine* engine = nullptr; +}; + +} // namespace + Local Function::newFunction(script::FunctionCallback callback) { - TEMPLATE_NOT_IMPLEMENTED(); + auto callbackIns = std::make_unique(); + callbackIns->engine = EngineScope::currentEngineAs(); + callbackIns->function = std::move(callback); + + PyMethodDef method{}; + method.ml_name = "ScriptX_native_method"; + method.ml_flags = METH_O; + method.ml_doc = "ScriptX Function::newFunction"; + method.ml_meth = [](PyObject* self, PyObject* args) -> PyObject* { + auto ptr = PyCapsule_GetPointer(self, kFunctionDataName); + if (ptr == nullptr) { + // TODO: exception + } else { + auto data = static_cast(ptr); + try { + auto ret = data->function(py_interop::makeArguments(nullptr, self, args)); + return py_interop::toPy(ret); + } catch (Exception& e) { + // TODO: exception + } + } + return nullptr; + }; + + auto ctx = PyCapsule_New(callbackIns.get(), kFunctionDataName, [](PyObject* cap) { + auto ptr = PyCapsule_GetPointer(cap, kFunctionDataName); + delete static_cast(ptr); + }); + + PyObject* closure = PyCFunction_New(&method, ctx); + + Py_XDECREF(ctx); + + // todo: check exception + callbackIns.release(); + return Local(closure); } Local Array::newArray(size_t size) { diff --git a/backend/Python/trait/TraitNative.h b/backend/Python/trait/TraitNative.h index 79f57f3e..3022f038 100644 --- a/backend/Python/trait/TraitNative.h +++ b/backend/Python/trait/TraitNative.h @@ -17,14 +17,16 @@ #pragma once #include "../../src/types.h" +#include "../PyHelper.h" namespace script { namespace py_backend { struct ArgumentsData { - int stackBase; - size_t size; + mutable PyEngine* engine; + PyObject* self; + PyObject* args; }; struct ScriptClassState { diff --git a/backend/Python/trait/TraitUtils.h b/backend/Python/trait/TraitUtils.h index 506a9c46..215f6802 100644 --- a/backend/Python/trait/TraitUtils.h +++ b/backend/Python/trait/TraitUtils.h @@ -20,9 +20,7 @@ namespace script { -namespace py_backend { struct py_interop; -} template <> struct internal::ImplType { @@ -31,7 +29,7 @@ struct internal::ImplType { template <> struct internal::ImplType { - using type = py_backend::py_interop; + using type = py_interop; }; } // namespace script \ No newline at end of file From d4faea7a703c8251b5e5a7f6f6cb1cfe1829e28f Mon Sep 17 00:00:00 2001 From: landerlyoung Date: Sun, 19 Dec 2021 23:50:50 +0800 Subject: [PATCH 10/10] wip --- backend/Python/PyHelper.cc | 11 ++++------- backend/Python/PyHelper.h | 2 +- backend/Python/PyHelper.hpp | 1 + backend/Python/PyLocalReference.cc | 26 +++++++++++++++++++++++--- backend/Python/PyValue.cc | 14 +++++++------- 5 files changed, 36 insertions(+), 18 deletions(-) diff --git a/backend/Python/PyHelper.cc b/backend/Python/PyHelper.cc index d02f4b0e..ced86204 100644 --- a/backend/Python/PyHelper.cc +++ b/backend/Python/PyHelper.cc @@ -26,13 +26,6 @@ PyObject* checkException(PyObject* obj) { return obj; } -int checkException(int ret) { - if (ret == -1) { - checkException(); - } - return ret; -} - void checkException() { auto err = PyErr_Occurred(); if (err) { @@ -40,4 +33,8 @@ void checkException() { } } +void rethrowException(const Exception& exception) { + // TODO +} + } // namespace script::py_backend \ No newline at end of file diff --git a/backend/Python/PyHelper.h b/backend/Python/PyHelper.h index 8158e146..fb9b5d81 100644 --- a/backend/Python/PyHelper.h +++ b/backend/Python/PyHelper.h @@ -37,7 +37,7 @@ namespace script::py_backend { class PyEngine; PyObject* checkException(PyObject* obj); -int checkException(int ret); void checkException(); +void rethrowException(const Exception& exception); } // namespace script::py_backend diff --git a/backend/Python/PyHelper.hpp b/backend/Python/PyHelper.hpp index 2197ed34..c3ef7e21 100644 --- a/backend/Python/PyHelper.hpp +++ b/backend/Python/PyHelper.hpp @@ -18,6 +18,7 @@ #pragma once #include "../../src/Native.hpp" #include "../../src/Reference.h" +#include "PyEngine.h" #include "PyHelper.h" namespace script { diff --git a/backend/Python/PyLocalReference.cc b/backend/Python/PyLocalReference.cc index d2ee9a6c..4bac19aa 100644 --- a/backend/Python/PyLocalReference.cc +++ b/backend/Python/PyLocalReference.cc @@ -19,6 +19,7 @@ #include "../../src/Reference.h" #include "../../src/Utils.h" #include "../../src/Value.h" +#include "PyHelper.hpp" #include "PyReference.hpp" namespace script { @@ -61,7 +62,7 @@ void valueConstructorCheck(PyObject* value) { std::string Local::describeUtf8() const { return asValue().describeUtf8(); } #define REF_IMPL_TO_VALUE(ValueType) \ - Local Local::asValue() const { return Local(val_); } + Local Local::asValue() const { return Local(py_backend::incRef(val_)); } REF_IMPL_BASIC_FUNC(Value) @@ -146,7 +147,7 @@ bool Local::isNumber() const { return PyNumber_Check(val_); } bool Local::isBoolean() const { return PyBool_Check(val_); } -bool Local::isFunction() const { return false; } +bool Local::isFunction() const { return PyCallable_Check(val_); } bool Local::isArray() const { return false; } @@ -217,7 +218,26 @@ bool Local::value() const { return false; } Local Local::callImpl(const Local& thiz, size_t size, const Local* args) const { - return {}; + // PyObject* self = thiz.isObject() ? py_interop::toPy(thiz) : nullptr; + // TODO: self + PyObject* ret = nullptr; + // args to tuple + if (size == 0) { + ret = PyObject_CallNoArgs(py_interop::asPy(*this)); + } else if (size == 1) { + ret = PyObject_CallOneArg(py_interop::asPy(*this), py_interop::asPy(args[0])); + } else { + auto tuple = PyTuple_New(static_cast(size)); + py_backend::checkException(); + for (size_t i = 0; i < size; ++i) { + PyTuple_SetItem(tuple, static_cast(i), py_interop::toPy(args[i])); + py_backend::checkException(); + } + ret = PyObject_Call(py_interop::asPy(*this), tuple, nullptr); + } + + py_backend::checkException(); + return Local(ret); } size_t Local::size() const { return 0; } diff --git a/backend/Python/PyValue.cc b/backend/Python/PyValue.cc index 11ba4e8f..3c8fda2e 100644 --- a/backend/Python/PyValue.cc +++ b/backend/Python/PyValue.cc @@ -21,8 +21,8 @@ #include "../../src/Value.h" #include "PyHelper.hpp" +using script::py_interop; using script::py_backend::checkException; -using script::py_backend::py_interop; namespace script { @@ -87,7 +87,7 @@ Local Boolean::newBoolean(bool value) { namespace { -static constexpr const char* kFunctionDataName = "capsule_function_data"; +static constexpr const char* kFunctionDataName = "_ScriptX_function_data"; struct FunctionData { FunctionCallback function; @@ -108,14 +108,14 @@ Local Function::newFunction(script::FunctionCallback callback) { method.ml_meth = [](PyObject* self, PyObject* args) -> PyObject* { auto ptr = PyCapsule_GetPointer(self, kFunctionDataName); if (ptr == nullptr) { - // TODO: exception + ::PyErr_SetString(PyExc_TypeError, "invalid 'self' for native method"); } else { auto data = static_cast(ptr); try { auto ret = data->function(py_interop::makeArguments(nullptr, self, args)); return py_interop::toPy(ret); } catch (Exception& e) { - // TODO: exception + py_backend::rethrowException(e); } } return nullptr; @@ -125,13 +125,13 @@ Local Function::newFunction(script::FunctionCallback callback) { auto ptr = PyCapsule_GetPointer(cap, kFunctionDataName); delete static_cast(ptr); }); + py_backend::checkException(ctx); + callbackIns.release(); PyObject* closure = PyCFunction_New(&method, ctx); - Py_XDECREF(ctx); + py_backend::checkException(closure); - // todo: check exception - callbackIns.release(); return Local(closure); }