mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-20 03:20:18 +00:00
commit
945407b3e3
4
.github/workflows/dylib.yml
vendored
4
.github/workflows/dylib.yml
vendored
@ -1,5 +1,7 @@
|
||||
name: build dylib
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
build_win:
|
||||
|
@ -12,6 +12,8 @@ aux_source_directory(${CMAKE_CURRENT_LIST_DIR}/src/common BOX2D_SRC_1)
|
||||
aux_source_directory(${CMAKE_CURRENT_LIST_DIR}/src/dynamics BOX2D_SRC_2)
|
||||
aux_source_directory(${CMAKE_CURRENT_LIST_DIR}/src/rope BOX2D_SRC_3)
|
||||
|
||||
aux_source_directory(${CMAKE_CURRENT_LIST_DIR}/src BOX2D_BINDINGS_SRC)
|
||||
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-O2")
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
@ -20,5 +22,5 @@ add_library(
|
||||
box2d
|
||||
STATIC
|
||||
${BOX2D_SRC_0} ${BOX2D_SRC_1} ${BOX2D_SRC_2} ${BOX2D_SRC_3}
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/box2d_bindings.cpp
|
||||
${BOX2D_BINDINGS_SRC}
|
||||
)
|
@ -1,8 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "box2d/b2_world.h"
|
||||
#include "box2d/box2d.h"
|
||||
#include "pocketpy/common.h"
|
||||
#include "pocketpy/pocketpy.h"
|
||||
#include <cstdlib>
|
||||
|
||||
namespace pkpy{
|
||||
|
||||
@ -22,126 +23,134 @@ inline PyObject* py_var(VM* vm, b2Vec2 v){
|
||||
return py_var(vm, Vec2(v.x, v.y));
|
||||
}
|
||||
|
||||
namespace imbox2d{
|
||||
inline PyObject* get_body_object(b2Body* p){
|
||||
auto userdata = p->GetUserData().pointer;
|
||||
return reinterpret_cast<PyObject*>(userdata);
|
||||
}
|
||||
|
||||
// maybe we will use this class later
|
||||
struct PyDebugDraw: b2Draw{
|
||||
PK_ALWAYS_PASS_BY_POINTER(PyDebugDraw)
|
||||
|
||||
VM* vm;
|
||||
PyObject* draw_like;
|
||||
|
||||
PyDebugDraw(VM* vm): vm(vm){}
|
||||
|
||||
void DrawPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) override{
|
||||
}
|
||||
|
||||
void DrawSolidPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) override{
|
||||
}
|
||||
|
||||
void DrawCircle(const b2Vec2& center, float radius, const b2Color& color) override{
|
||||
}
|
||||
|
||||
void DrawSolidCircle(const b2Vec2& center, float radius, const b2Vec2& axis, const b2Color& color) override{
|
||||
}
|
||||
|
||||
void DrawSegment(const b2Vec2& p1, const b2Vec2& p2, const b2Color& color) override{
|
||||
}
|
||||
|
||||
void DrawTransform(const b2Transform& xf) override{
|
||||
}
|
||||
|
||||
void DrawPoint(const b2Vec2& p, float size, const b2Color& color) override{
|
||||
}
|
||||
};
|
||||
|
||||
struct PyContactListener: b2ContactListener{
|
||||
PK_ALWAYS_PASS_BY_POINTER(PyContactListener)
|
||||
VM* vm;
|
||||
PyContactListener(VM* vm): vm(vm){}
|
||||
|
||||
void _contact_f(b2Contact* contact, StrName name){
|
||||
b2Body* bodyA = contact->GetFixtureA()->GetBody();
|
||||
b2Body* bodyB = contact->GetFixtureB()->GetBody();
|
||||
PyObject* a = get_body_object(bodyA);
|
||||
PyObject* b = get_body_object(bodyB);
|
||||
PyObject* self;
|
||||
PyObject* f;
|
||||
f = vm->get_unbound_method(a, name, &self, false);
|
||||
if(f != nullptr) vm->call_method(self, f, b);
|
||||
f = vm->get_unbound_method(b, name, &self, false);
|
||||
if(f != nullptr) vm->call_method(self, f, a);
|
||||
}
|
||||
|
||||
void BeginContact(b2Contact* contact) override {
|
||||
DEF_SNAME(on_contact_begin);
|
||||
_contact_f(contact, on_contact_begin);
|
||||
}
|
||||
|
||||
void EndContact(b2Contact* contact) override {
|
||||
DEF_SNAME(on_contact_end);
|
||||
_contact_f(contact, on_contact_end);
|
||||
}
|
||||
};
|
||||
|
||||
struct PyBody{
|
||||
PY_CLASS(PyBody, box2d, Body)
|
||||
PK_ALWAYS_PASS_BY_POINTER(PyBody)
|
||||
|
||||
struct Body final{
|
||||
b2Body* body;
|
||||
b2Fixture* fixture;
|
||||
PyObject* obj;
|
||||
Vec4 debug_color;
|
||||
PyObject* node_like;
|
||||
|
||||
Body(b2World* world, PyObject* obj){
|
||||
b2BodyDef def;
|
||||
def.type = b2_dynamicBody;
|
||||
// a weak reference to the object, no need to mark it
|
||||
def.userData.pointer = reinterpret_cast<uintptr_t>(this);
|
||||
body = world->CreateBody(&def);
|
||||
fixture = nullptr;
|
||||
this->obj = obj;
|
||||
this->debug_color = Vec4(std::rand() / float(RAND_MAX), std::rand() / float(RAND_MAX), std::rand() / float(RAND_MAX), 1.0f);
|
||||
PyBody(): body(nullptr), fixture(nullptr), node_like(nullptr){}
|
||||
|
||||
void _gc_mark() {
|
||||
PK_OBJ_MARK(node_like);
|
||||
}
|
||||
|
||||
void _update_fixture(b2Shape* shape){
|
||||
body->DestroyFixture(fixture); // this takes care of NULL case
|
||||
fixture = body->CreateFixture(shape, 1.0f);
|
||||
}
|
||||
PyBody& _() { return *this; }
|
||||
b2Body& _b2Body() { return *body; }
|
||||
b2Fixture& _b2Fixture() { return *fixture; }
|
||||
|
||||
Vec4 get_debug_color() const{ return debug_color; }
|
||||
|
||||
b2Vec2 get_position() const{ return body->GetPosition(); }
|
||||
void set_position(b2Vec2 v){ body->SetTransform(v, get_rotation()); }
|
||||
|
||||
void set_rotation(float angle){ body->SetTransform(get_position(), angle); }
|
||||
float get_rotation() const{ return body->GetAngle(); }
|
||||
|
||||
void set_velocity(b2Vec2 v){ body->SetLinearVelocity(v); }
|
||||
b2Vec2 get_velocity() const{ return body->GetLinearVelocity(); }
|
||||
|
||||
void set_angular_velocity(float omega){ body->SetAngularVelocity(omega); }
|
||||
float get_angular_velocity() const{ return body->GetAngularVelocity(); }
|
||||
|
||||
void set_damping(float damping){ body->SetLinearDamping(damping); }
|
||||
float get_damping(){ return body->GetLinearDamping(); }
|
||||
|
||||
void set_angular_damping(float damping){ body->SetAngularDamping(damping); }
|
||||
float get_angular_damping() const{ return body->GetAngularDamping(); }
|
||||
|
||||
void set_gravity_scale(float scale){ body->SetGravityScale(scale); }
|
||||
float get_gravity_scale() const{ return body->GetGravityScale(); }
|
||||
|
||||
void set_type(int type){ body->SetType(static_cast<b2BodyType>(type)); }
|
||||
int get_type() const{ return static_cast<int>(body->GetType()); }
|
||||
|
||||
float get_mass() const{ return body->GetMass(); }
|
||||
float get_inertia() const{ return body->GetInertia(); }
|
||||
|
||||
bool get_fixed_rotation() const{ return body->IsFixedRotation(); }
|
||||
void set_fixed_rotation(bool fixed){ body->SetFixedRotation(fixed); }
|
||||
|
||||
// fixture settings
|
||||
float get_density() const{ return fixture->GetDensity(); }
|
||||
void set_density(float density){ fixture->SetDensity(density); }
|
||||
|
||||
float get_friction() const{ return fixture->GetFriction(); }
|
||||
void set_friction(float friction){ fixture->SetFriction(friction); }
|
||||
|
||||
float get_restitution() const{ return fixture->GetRestitution(); }
|
||||
void set_restitution(float restitution){ fixture->SetRestitution(restitution); }
|
||||
|
||||
float get_restitution_threshold() const{ return fixture->GetRestitutionThreshold(); }
|
||||
void set_restitution_threshold(float threshold){ fixture->SetRestitutionThreshold(threshold); }
|
||||
|
||||
bool get_is_trigger() const{ return fixture->IsSensor(); }
|
||||
void set_is_trigger(bool trigger){ fixture->SetSensor(trigger); }
|
||||
static void _register(VM* vm, PyObject* mod, PyObject* type);
|
||||
|
||||
// methods
|
||||
void apply_force(b2Vec2 force, b2Vec2 point){
|
||||
body->ApplyForce(force, point, true);
|
||||
}
|
||||
b2Vec2 get_position() const { return body->GetPosition(); }
|
||||
void set_position(b2Vec2 v){ body->SetTransform(v, body->GetAngle()); }
|
||||
float get_rotation() const { return body->GetAngle(); }
|
||||
void set_rotation(float v){ body->SetTransform(body->GetPosition(), v); }
|
||||
b2Vec2 get_velocity() const { return body->GetLinearVelocity(); }
|
||||
void set_velocity(b2Vec2 v){ body->SetLinearVelocity(v); }
|
||||
|
||||
void apply_force_to_center(b2Vec2 force){
|
||||
body->ApplyForceToCenter(force, true);
|
||||
}
|
||||
|
||||
void apply_torque(float torque){
|
||||
body->ApplyTorque(torque, true);
|
||||
}
|
||||
|
||||
void apply_linear_impulse(b2Vec2 impulse, b2Vec2 point){
|
||||
void apply_force(b2Vec2 force, b2Vec2 point){ body->ApplyForce(force, point, true); }
|
||||
void apply_force_to_center(b2Vec2 force){ body->ApplyForceToCenter(force, true); }
|
||||
void apply_torque(float torque){ body->ApplyTorque(torque, true); }
|
||||
void apply_impulse(b2Vec2 impulse, b2Vec2 point){
|
||||
body->ApplyLinearImpulse(impulse, point, true);
|
||||
}
|
||||
|
||||
void apply_linear_impulse_to_center(b2Vec2 impulse){
|
||||
void apply_impulse_to_center(b2Vec2 impulse){
|
||||
body->ApplyLinearImpulseToCenter(impulse, true);
|
||||
}
|
||||
|
||||
void apply_angular_impulse(float impulse){
|
||||
body->ApplyAngularImpulse(impulse, true);
|
||||
}
|
||||
|
||||
void destroy(){
|
||||
if(body == nullptr) return;
|
||||
body->GetWorld()->DestroyBody(body);
|
||||
body = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
struct PyBody: OpaquePointer<Body>{
|
||||
PY_CLASS(PyBody, box2d, Body)
|
||||
|
||||
using OpaquePointer<Body>::OpaquePointer;
|
||||
static void _register(VM* vm, PyObject* mod, PyObject* type);
|
||||
};
|
||||
|
||||
struct PyWorld: OpaquePointer<b2World>{
|
||||
struct PyWorld {
|
||||
PY_CLASS(PyWorld, box2d, World)
|
||||
PK_ALWAYS_PASS_BY_POINTER(PyWorld)
|
||||
|
||||
b2World world;
|
||||
PyContactListener _contact_listener;
|
||||
PyDebugDraw _debug_draw;
|
||||
|
||||
PyWorld(VM* vm);
|
||||
|
||||
void _gc_mark(){
|
||||
PK_OBJ_MARK(_debug_draw.draw_like);
|
||||
}
|
||||
|
||||
using OpaquePointer<b2World>::OpaquePointer;
|
||||
static void _register(VM* vm, PyObject* mod, PyObject* type);
|
||||
};
|
||||
|
||||
} // namespace imbox2d
|
||||
|
||||
void add_module_box2d(VM* vm);
|
||||
inline void add_module_box2d(VM* vm){
|
||||
PyObject* mod = vm->new_module("box2d");
|
||||
PyBody::register_class(vm, mod);
|
||||
PyWorld::register_class(vm, mod);
|
||||
}
|
||||
|
||||
} // namespace pkpy
|
86
3rd/box2d/src/box2d_Body.cpp
Normal file
86
3rd/box2d/src/box2d_Body.cpp
Normal file
@ -0,0 +1,86 @@
|
||||
#include "box2d/b2_world.h"
|
||||
#include "box2d/b2_world_callbacks.h"
|
||||
#include "box2d_bindings.hpp"
|
||||
#include "pocketpy/bindings.h"
|
||||
|
||||
namespace pkpy{
|
||||
|
||||
void PyBody::_register(VM* vm, PyObject* mod, PyObject* type){
|
||||
vm->bind(type, "__new__(cls, world: World, node: _NodeLike = None)",
|
||||
[](VM* vm, ArgsView args){
|
||||
PyWorld& world = CAST(PyWorld&, args[1]);
|
||||
PyObject* node = args[2];
|
||||
PyObject* obj = vm->heap.gcnew<PyBody>(PyBody::_type(vm));
|
||||
PyBody& body = _CAST(PyBody&, obj);
|
||||
b2BodyDef def;
|
||||
def.type = b2_dynamicBody;
|
||||
// a weak reference to this object
|
||||
def.userData.pointer = reinterpret_cast<uintptr_t>(obj);
|
||||
body.body = world.world.CreateBody(&def);
|
||||
body.fixture = nullptr;
|
||||
body.node_like = node;
|
||||
return obj;
|
||||
});
|
||||
|
||||
PK_REGISTER_PROPERTY(PyBody, "type: int", _b2Body, GetType, SetType)
|
||||
PK_REGISTER_PROPERTY(PyBody, "gravity_scale: float", _b2Body, GetGravityScale, SetGravityScale)
|
||||
PK_REGISTER_PROPERTY(PyBody, "fixed_rotation: bool", _b2Body, IsFixedRotation, SetFixedRotation)
|
||||
PK_REGISTER_PROPERTY(PyBody, "enabled: bool", _b2Body, IsEnabled, SetEnabled)
|
||||
PK_REGISTER_PROPERTY(PyBody, "bullet: bool", _b2Body, IsBullet, SetBullet)
|
||||
|
||||
PK_REGISTER_READONLY_PROPERTY(PyBody, "mass: float", _b2Body, GetMass)
|
||||
PK_REGISTER_READONLY_PROPERTY(PyBody, "inertia: float", _b2Body, GetInertia)
|
||||
|
||||
PK_REGISTER_PROPERTY(PyBody, "position: vec2", _, get_position, set_position)
|
||||
PK_REGISTER_PROPERTY(PyBody, "rotation: float", _, get_rotation, set_rotation)
|
||||
PK_REGISTER_PROPERTY(PyBody, "velocity: vec2", _, get_velocity, set_velocity)
|
||||
PK_REGISTER_PROPERTY(PyBody, "angular_velocity: float", _b2Body, GetAngularVelocity, SetAngularVelocity)
|
||||
PK_REGISTER_PROPERTY(PyBody, "damping: float", _b2Body, GetLinearDamping, SetLinearDamping)
|
||||
PK_REGISTER_PROPERTY(PyBody, "angular_damping: float", _b2Body, GetAngularDamping, SetAngularDamping)
|
||||
|
||||
PK_REGISTER_PROPERTY(PyBody, "density: float", _b2Fixture, GetDensity, SetDensity)
|
||||
PK_REGISTER_PROPERTY(PyBody, "friction: float", _b2Fixture, GetFriction, SetFriction)
|
||||
PK_REGISTER_PROPERTY(PyBody, "restitution: float", _b2Fixture, GetRestitution, SetRestitution)
|
||||
PK_REGISTER_PROPERTY(PyBody, "restitution_threshold: float", _b2Fixture, GetRestitutionThreshold, SetRestitutionThreshold)
|
||||
PK_REGISTER_PROPERTY(PyBody, "is_trigger: bool", _b2Fixture, IsSensor, SetSensor)
|
||||
|
||||
// methods
|
||||
_bind(vm, type, "apply_force(self, force: vec2, point: vec2)", &PyBody::apply_force);
|
||||
_bind(vm, type, "apply_force_to_center(self, force: vec2)", &PyBody::apply_force_to_center);
|
||||
_bind(vm, type, "apply_torque(self, torque: float)", &PyBody::apply_torque);
|
||||
_bind(vm, type, "apply_impulse(self, impulse: vec2, point: vec2)", &PyBody::apply_impulse);
|
||||
_bind(vm, type, "apply_impulse_to_center(self, impulse: vec2)", &PyBody::apply_impulse_to_center);
|
||||
_bind(vm, type, "apply_angular_impulse(self, impulse: float)", &PyBody::apply_angular_impulse);
|
||||
|
||||
// get_node
|
||||
vm->bind(type, "get_node(self)", [](VM* vm, ArgsView args){
|
||||
PyBody& body = CAST(PyBody&, args[1]);
|
||||
return body.node_like;
|
||||
});
|
||||
|
||||
// get_contacts
|
||||
vm->bind(type, "get_contacts(self) -> list[Body]", [](VM* vm, ArgsView args){
|
||||
PyBody& self = _CAST(PyBody&, args[0]);
|
||||
b2ContactEdge* edge = self.body->GetContactList();
|
||||
List list;
|
||||
while(edge){
|
||||
b2Fixture* fixtureB = edge->contact->GetFixtureB();
|
||||
b2Body* bodyB = fixtureB->GetBody();
|
||||
list.push_back(get_body_object(bodyB));
|
||||
edge = edge->next;
|
||||
}
|
||||
return VAR(std::move(list));
|
||||
});
|
||||
|
||||
// destroy
|
||||
vm->bind(type, "destroy(self)", [](VM* vm, ArgsView args){
|
||||
PyBody& body = CAST(PyBody&, args[1]);
|
||||
body.body->GetWorld()->DestroyBody(body.body);
|
||||
body.body = nullptr;
|
||||
body.fixture = nullptr;
|
||||
body.node_like = nullptr;
|
||||
return vm->None;
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace pkpy
|
130
3rd/box2d/src/box2d_World.cpp
Normal file
130
3rd/box2d/src/box2d_World.cpp
Normal file
@ -0,0 +1,130 @@
|
||||
#include "box2d/b2_world.h"
|
||||
#include "box2d/b2_world_callbacks.h"
|
||||
#include "box2d_bindings.hpp"
|
||||
|
||||
namespace pkpy{
|
||||
|
||||
struct MyRayCastCallback: b2RayCastCallback{
|
||||
PK_ALWAYS_PASS_BY_POINTER(MyRayCastCallback)
|
||||
|
||||
VM* vm;
|
||||
List result;
|
||||
MyRayCastCallback(VM* vm): vm(vm) {}
|
||||
|
||||
float ReportFixture(b2Fixture* fixture, const b2Vec2& point, const b2Vec2& normal, float fraction){
|
||||
result.push_back(get_body_object(fixture->GetBody()));
|
||||
// if(only_one) return 0;
|
||||
return fraction;
|
||||
}
|
||||
};
|
||||
|
||||
struct MyBoxCastCallback: b2QueryCallback{
|
||||
PK_ALWAYS_PASS_BY_POINTER(MyBoxCastCallback)
|
||||
|
||||
VM* vm;
|
||||
List result;
|
||||
MyBoxCastCallback(VM* vm): vm(vm) {}
|
||||
|
||||
bool ReportFixture(b2Fixture* fixture) override{
|
||||
result.push_back(get_body_object(fixture->GetBody()));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/****************** PyWorld ******************/
|
||||
PyWorld::PyWorld(VM* vm): world(b2Vec2(0, 0)), _contact_listener(vm), _debug_draw(vm){
|
||||
_debug_draw.draw_like = vm->None;
|
||||
world.SetAllowSleeping(true);
|
||||
world.SetAutoClearForces(true);
|
||||
world.SetContactListener(&_contact_listener);
|
||||
world.SetDebugDraw(&_debug_draw);
|
||||
}
|
||||
|
||||
void PyWorld::_register(VM* vm, PyObject* mod, PyObject* type){
|
||||
vm->bind(type, "__new__(cls)", [](VM* vm, ArgsView args){
|
||||
return vm->heap.gcnew<PyWorld>(PyWorld::_type(vm), vm);
|
||||
});
|
||||
|
||||
// gravity
|
||||
vm->bind_property(type, "gravity: vec2", [](VM* vm, ArgsView args){
|
||||
PyWorld& self = _CAST(PyWorld&, args[0]);
|
||||
return VAR(self.world.GetGravity());
|
||||
}, [](VM* vm, ArgsView args){
|
||||
PyWorld& self = _CAST(PyWorld&, args[0]);
|
||||
self.world.SetGravity(CAST(b2Vec2, args[1]));
|
||||
return vm->None;
|
||||
});
|
||||
|
||||
vm->bind(type, "get_bodies(self) -> list[Body]", [](VM* vm, ArgsView args){
|
||||
PyWorld& self = _CAST(PyWorld&, args[0]);
|
||||
List list;
|
||||
b2Body* p = self.world.GetBodyList();
|
||||
while(p != nullptr){
|
||||
list.push_back(get_body_object(p));
|
||||
p = p->GetNext();
|
||||
}
|
||||
return VAR(std::move(list));
|
||||
});
|
||||
|
||||
vm->bind(type, "ray_cast(self, start: vec2, end: vec2) -> list[Body]", [](VM* vm, ArgsView args){
|
||||
auto _lock = vm->heap.gc_scope_lock();
|
||||
PyWorld& self = _CAST(PyWorld&, args[0]);
|
||||
b2Vec2 start = CAST(b2Vec2, args[1]);
|
||||
b2Vec2 end = CAST(b2Vec2, args[2]);
|
||||
MyRayCastCallback callback(vm);
|
||||
self.world.RayCast(&callback, start, end);
|
||||
return VAR(std::move(callback.result));
|
||||
});
|
||||
|
||||
vm->bind(type, "box_cast(self, lower: vec2, upper: vec2) -> list[Body]", [](VM* vm, ArgsView args){
|
||||
auto _lock = vm->heap.gc_scope_lock();
|
||||
PyWorld& self = _CAST(PyWorld&, args[0]);
|
||||
b2AABB aabb;
|
||||
aabb.lowerBound = CAST(b2Vec2, args[1]);
|
||||
aabb.upperBound = CAST(b2Vec2, args[2]);
|
||||
MyBoxCastCallback callback(vm);
|
||||
self.world.QueryAABB(&callback, aabb);
|
||||
return VAR(std::move(callback.result));
|
||||
});
|
||||
|
||||
vm->bind(type, "step(self, dt: float, velocity_iterations: int, position_iterations: int)",
|
||||
[](VM* vm, ArgsView args){
|
||||
PyWorld& self = _CAST(PyWorld&, args[0]);
|
||||
float dt = CAST(float, args[1]);
|
||||
int velocity_iterations = CAST(int, args[2]);
|
||||
int position_iterations = CAST(int, args[3]);
|
||||
|
||||
auto f = [](VM* vm, b2Body* p, StrName name){
|
||||
while(p != nullptr){
|
||||
PyObject* body_obj = get_body_object(p);
|
||||
PyBody& body = _CAST(PyBody&, body_obj);
|
||||
if(body.node_like != vm->None){
|
||||
vm->call_method(body.node_like, name);
|
||||
}
|
||||
p = p->GetNext();
|
||||
}
|
||||
};
|
||||
|
||||
DEF_SNAME(on_box2d_pre_step);
|
||||
DEF_SNAME(on_box2d_post_step);
|
||||
f(vm, self.world.GetBodyList(), on_box2d_pre_step);
|
||||
self.world.Step(dt, velocity_iterations, position_iterations);
|
||||
f(vm, self.world.GetBodyList(), on_box2d_post_step);
|
||||
return vm->None;
|
||||
});
|
||||
|
||||
vm->bind(type, "debug_draw(self, flags: int)", [](VM* vm, ArgsView args){
|
||||
PyWorld& self = _CAST(PyWorld&, args[0]);
|
||||
int flags = CAST(int, args[1]);
|
||||
self._debug_draw.SetFlags(flags);
|
||||
self.world.DebugDraw();
|
||||
return vm->None;
|
||||
});
|
||||
|
||||
vm->bind(type, "set_debug_draw(self, draw: _DrawLike)", [](VM* vm, ArgsView args){
|
||||
PyWorld& self = _CAST(PyWorld&, args[0]);
|
||||
self._debug_draw.draw_like = args[1];
|
||||
return vm->None;
|
||||
});
|
||||
}
|
||||
} // namespace pkpy
|
@ -1,321 +0,0 @@
|
||||
#include "box2d_bindings.hpp"
|
||||
|
||||
namespace pkpy{
|
||||
|
||||
void add_module_box2d(VM *vm){
|
||||
PyObject* mod = vm->new_module("box2d");
|
||||
imbox2d::PyBody::register_class(vm, mod);
|
||||
imbox2d::PyWorld::register_class(vm, mod);
|
||||
}
|
||||
|
||||
namespace imbox2d{
|
||||
|
||||
// maybe we will use this class later
|
||||
class PyDebugDraw: public b2Draw{
|
||||
VM* vm;
|
||||
public:
|
||||
PyDebugDraw(VM* vm): vm(vm){}
|
||||
|
||||
void DrawPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) override{
|
||||
}
|
||||
|
||||
void DrawSolidPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) override{
|
||||
}
|
||||
|
||||
void DrawCircle(const b2Vec2& center, float radius, const b2Color& color) override{
|
||||
}
|
||||
|
||||
void DrawSolidCircle(const b2Vec2& center, float radius, const b2Vec2& axis, const b2Color& color) override{
|
||||
}
|
||||
|
||||
void DrawSegment(const b2Vec2& p1, const b2Vec2& p2, const b2Color& color) override{
|
||||
}
|
||||
|
||||
void DrawTransform(const b2Transform& xf) override{
|
||||
}
|
||||
|
||||
void DrawPoint(const b2Vec2& p, float size, const b2Color& color) override{
|
||||
}
|
||||
};
|
||||
|
||||
class PyContactListener : public b2ContactListener{
|
||||
VM* vm;
|
||||
public:
|
||||
PyContactListener(VM* vm): vm(vm){}
|
||||
|
||||
void _contact_f(b2Contact* contact, StrName name){
|
||||
auto a = contact->GetFixtureA()->GetBody()->GetUserData().pointer;
|
||||
auto b = contact->GetFixtureB()->GetBody()->GetUserData().pointer;
|
||||
Body* bodyA = reinterpret_cast<Body*>(a);
|
||||
Body* bodyB = reinterpret_cast<Body*>(b);
|
||||
PyObject* self;
|
||||
PyObject* f;
|
||||
f = vm->get_unbound_method(bodyA->obj, name, &self, false);
|
||||
if(f != nullptr) vm->call_method(self, f, VAR_T(PyBody, bodyB));
|
||||
f = vm->get_unbound_method(bodyB->obj, name, &self, false);
|
||||
if(f != nullptr) vm->call_method(self, f, VAR_T(PyBody, bodyA));
|
||||
}
|
||||
|
||||
void BeginContact(b2Contact* contact) override {
|
||||
DEF_SNAME(on_contact_begin);
|
||||
_contact_f(contact, on_contact_begin);
|
||||
}
|
||||
|
||||
void EndContact(b2Contact* contact) override {
|
||||
DEF_SNAME(on_contact_end);
|
||||
_contact_f(contact, on_contact_end);
|
||||
}
|
||||
};
|
||||
|
||||
void PyBody::_register(VM* vm, PyObject* mod, PyObject* type){
|
||||
vm->bind_notimplemented_constructor<PyBody>(type);
|
||||
PK_REGISTER_READONLY_PROPERTY(PyBody, debug_color, "vec4");
|
||||
|
||||
PK_REGISTER_PROPERTY(PyBody, position, "vec2");
|
||||
PK_REGISTER_PROPERTY(PyBody, rotation, "float");
|
||||
PK_REGISTER_PROPERTY(PyBody, velocity, "vec2");
|
||||
PK_REGISTER_PROPERTY(PyBody, angular_velocity, "float");
|
||||
PK_REGISTER_PROPERTY(PyBody, damping, "float");
|
||||
PK_REGISTER_PROPERTY(PyBody, angular_damping, "float");
|
||||
PK_REGISTER_PROPERTY(PyBody, gravity_scale, "float");
|
||||
PK_REGISTER_PROPERTY(PyBody, type, "int");
|
||||
PK_REGISTER_READONLY_PROPERTY(PyBody, mass, "float");
|
||||
PK_REGISTER_READONLY_PROPERTY(PyBody, inertia, "float");
|
||||
|
||||
// fixture settings
|
||||
PK_REGISTER_PROPERTY(PyBody, density, "float");
|
||||
PK_REGISTER_PROPERTY(PyBody, friction, "float");
|
||||
PK_REGISTER_PROPERTY(PyBody, restitution, "float");
|
||||
PK_REGISTER_PROPERTY(PyBody, restitution_threshold, "float");
|
||||
PK_REGISTER_PROPERTY(PyBody, is_trigger, "bool");
|
||||
|
||||
// methods
|
||||
_bind_opaque<PyBody>(vm, type, "apply_force(self, force: vec2, point: vec2)", &Body::apply_force);
|
||||
_bind_opaque<PyBody>(vm, type, "apply_force_to_center(self, force: vec2)", &Body::apply_force_to_center);
|
||||
_bind_opaque<PyBody>(vm, type, "apply_torque(self, torque: float)", &Body::apply_torque);
|
||||
_bind_opaque<PyBody>(vm, type, "apply_linear_impulse(self, impulse: vec2, point: vec2)", &Body::apply_linear_impulse);
|
||||
_bind_opaque<PyBody>(vm, type, "apply_linear_impulse_to_center(self, impulse: vec2)", &Body::apply_linear_impulse_to_center);
|
||||
_bind_opaque<PyBody>(vm, type, "apply_angular_impulse(self, impulse: float)", &Body::apply_angular_impulse);
|
||||
|
||||
vm->bind__eq__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* lhs, PyObject* rhs){
|
||||
PyBody& self = _CAST(PyBody&, lhs);
|
||||
if(is_non_tagged_type(rhs, PyBody::_type(vm))) return vm->NotImplemented;
|
||||
PyBody& other = _CAST(PyBody&, rhs);
|
||||
return VAR(self->body == other->body);
|
||||
});
|
||||
|
||||
vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){
|
||||
PyBody& self = _CAST(PyBody&, obj);
|
||||
return VAR(fmt("<Body* at ", self->body, ">"));
|
||||
});
|
||||
|
||||
// destroy
|
||||
_bind_opaque<PyBody>(vm, type, "destroy(self)", &Body::destroy);
|
||||
|
||||
// contacts
|
||||
vm->bind(type, "get_contacts(self) -> list", [](VM* vm, ArgsView args){
|
||||
PyBody& self = _CAST(PyBody&, args[0]);
|
||||
b2ContactEdge* edge = self->body->GetContactList();
|
||||
List list;
|
||||
while(edge){
|
||||
b2Fixture* fixtureB = edge->contact->GetFixtureB();
|
||||
b2Body* bodyB = fixtureB->GetBody();
|
||||
PyObject* objB = reinterpret_cast<Body*>(bodyB->GetUserData().pointer)->obj;
|
||||
list.push_back(objB);
|
||||
edge = edge->next;
|
||||
}
|
||||
return VAR(std::move(list));
|
||||
});
|
||||
|
||||
// userdata
|
||||
vm->bind(type, "get_node(self)", [](VM* vm, ArgsView args){
|
||||
PyBody& self = _CAST(PyBody&, args[0]);
|
||||
return self->obj;
|
||||
});
|
||||
|
||||
// shape
|
||||
vm->bind(type, "set_box_shape(self, hx: float, hy: float)", [](VM* vm, ArgsView args){
|
||||
PyBody& self = _CAST(PyBody&, args[0]);
|
||||
float hx = CAST(float, args[1]);
|
||||
float hy = CAST(float, args[2]);
|
||||
b2PolygonShape shape;
|
||||
shape.SetAsBox(hx, hy);
|
||||
self->_update_fixture(&shape);
|
||||
return vm->None;
|
||||
});
|
||||
|
||||
vm->bind(type, "set_circle_shape(self, radius: float)", [](VM* vm, ArgsView args){
|
||||
PyBody& self = _CAST(PyBody&, args[0]);
|
||||
float radius = CAST(float, args[1]);
|
||||
b2CircleShape shape;
|
||||
shape.m_radius = radius;
|
||||
self->_update_fixture(&shape);
|
||||
return vm->None;
|
||||
});
|
||||
|
||||
vm->bind(type, "set_polygon_shape(self, points: list[vec2])", [](VM* vm, ArgsView args){
|
||||
PyBody& self = _CAST(PyBody&, args[0]);
|
||||
List& points = CAST(List&, args[1]);
|
||||
if(points.size() > b2_maxPolygonVertices || points.size() < 3){
|
||||
vm->ValueError(fmt("invalid polygon vertices count: ", points.size()));
|
||||
return vm->None;
|
||||
}
|
||||
std::vector<b2Vec2> vertices(points.size());
|
||||
for(int i = 0; i < points.size(); ++i){
|
||||
vertices[i] = CAST(b2Vec2, points[i]);
|
||||
}
|
||||
b2PolygonShape shape;
|
||||
shape.Set(vertices.data(), vertices.size());
|
||||
self->_update_fixture(&shape);
|
||||
return vm->None;
|
||||
});
|
||||
|
||||
vm->bind(type, "set_chain_shape(self, points: list[vec2])", [](VM* vm, ArgsView args){
|
||||
PyBody& self = _CAST(PyBody&, args[0]);
|
||||
List& points = CAST(List&, args[1]);
|
||||
std::vector<b2Vec2> vertices(points.size());
|
||||
for(int i = 0; i < points.size(); ++i){
|
||||
vertices[i] = CAST(b2Vec2, points[i]);
|
||||
}
|
||||
b2ChainShape shape;
|
||||
shape.CreateLoop(vertices.data(), vertices.size());
|
||||
self->_update_fixture(&shape);
|
||||
return vm->None;
|
||||
});
|
||||
|
||||
vm->bind(type, "get_shape_info(self) -> tuple", [](VM* vm, ArgsView args){
|
||||
PyBody& self = _CAST(PyBody&, args[0]);
|
||||
b2Shape* shape = self->fixture->GetShape();
|
||||
switch(shape->GetType()){
|
||||
case b2Shape::e_polygon:{
|
||||
b2PolygonShape* poly = static_cast<b2PolygonShape*>(shape);
|
||||
Tuple points(poly->m_count + 1);
|
||||
for(int i = 0; i < poly->m_count; ++i){
|
||||
points[i] = VAR(poly->m_vertices[i]);
|
||||
}
|
||||
points[poly->m_count] = points[0];
|
||||
return VAR(Tuple({
|
||||
VAR("polygon"), VAR(std::move(points))
|
||||
}));
|
||||
}
|
||||
case b2Shape::e_circle:{
|
||||
b2CircleShape* circle = static_cast<b2CircleShape*>(shape);
|
||||
return VAR(Tuple({
|
||||
VAR("circle"), VAR(circle->m_radius)
|
||||
}));
|
||||
}
|
||||
case b2Shape::e_chain:{
|
||||
b2ChainShape* chain = static_cast<b2ChainShape*>(shape);
|
||||
Tuple points(chain->m_count);
|
||||
for(int i = 0; i < chain->m_count; ++i){
|
||||
points[i] = VAR(chain->m_vertices[i]);
|
||||
}
|
||||
return VAR(Tuple({
|
||||
VAR("chain"), VAR(std::move(points))
|
||||
}));
|
||||
}
|
||||
default:
|
||||
vm->ValueError("unsupported shape type");
|
||||
return vm->None;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// This class captures the closest hit shape.
|
||||
class MyRayCastCallback : public b2RayCastCallback
|
||||
{
|
||||
VM* vm;
|
||||
public:
|
||||
List result;
|
||||
MyRayCastCallback(VM* vm): vm(vm) {}
|
||||
|
||||
float ReportFixture(b2Fixture* fixture, const b2Vec2& point,
|
||||
const b2Vec2& normal, float fraction)
|
||||
{
|
||||
auto userdata = fixture->GetBody()->GetUserData().pointer;
|
||||
Body* body = reinterpret_cast<Body*>(userdata);
|
||||
result.push_back(VAR_T(PyBody, body));
|
||||
// if(only_one) return 0;
|
||||
return fraction;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void PyWorld::_register(VM* vm, PyObject* mod, PyObject* type){
|
||||
vm->bind(type, "__new__(cls)", [](VM* vm, ArgsView args){
|
||||
b2World* w = new b2World(b2Vec2(0, 0));
|
||||
w->SetAllowSleeping(true);
|
||||
w->SetAutoClearForces(true);
|
||||
// the contact listener will leak memory after the world is destroyed
|
||||
// but it's not a big deal since the world is only destroyed when the game exits
|
||||
w->SetContactListener(new PyContactListener(vm));
|
||||
w->SetDebugDraw(new PyDebugDraw(vm));
|
||||
return VAR_T(PyWorld, w);
|
||||
});
|
||||
|
||||
// gravity
|
||||
vm->bind_property(type, "gravity", "vec2", [](VM* vm, ArgsView args){
|
||||
PyWorld& self = _CAST(PyWorld&, args[0]);
|
||||
return VAR(self->GetGravity());
|
||||
}, [](VM* vm, ArgsView args){
|
||||
PyWorld& self = _CAST(PyWorld&, args[0]);
|
||||
self->SetGravity(CAST(b2Vec2, args[1]));
|
||||
return vm->None;
|
||||
});
|
||||
|
||||
// body
|
||||
vm->bind(type, "create_body(self, obj) -> Body", [](VM* vm, ArgsView args){
|
||||
PyWorld& self = _CAST(PyWorld&, args[0]);
|
||||
return VAR_T(PyBody, new Body(self.ptr, args[1]));
|
||||
});
|
||||
vm->bind(type, "get_bodies(self) -> list[Body]", [](VM* vm, ArgsView args){
|
||||
PyWorld& self = _CAST(PyWorld&, args[0]);
|
||||
List list;
|
||||
b2Body* p = self->GetBodyList();
|
||||
while(p != nullptr){
|
||||
Body* body = (Body*)p->GetUserData().pointer;
|
||||
list.push_back(VAR_T(PyBody, body));
|
||||
p = p->GetNext();
|
||||
}
|
||||
return VAR(std::move(list));
|
||||
});
|
||||
|
||||
// step
|
||||
vm->bind(type, "step(self, dt: float, velocity_iterations: int, position_iterations: int)",
|
||||
[](VM* vm, ArgsView args){
|
||||
PyWorld& self = _CAST(PyWorld&, args[0]);
|
||||
float dt = CAST(float, args[1]);
|
||||
int velocity_iterations = CAST(int, args[2]);
|
||||
int position_iterations = CAST(int, args[3]);
|
||||
|
||||
auto f = [](VM* vm, b2Body* p, StrName name){
|
||||
while(p != nullptr){
|
||||
Body* body = (Body*)p->GetUserData().pointer;
|
||||
vm->call_method(body->obj, name);
|
||||
p = p->GetNext();
|
||||
}
|
||||
};
|
||||
|
||||
DEF_SNAME(on_box2d_pre_step);
|
||||
DEF_SNAME(on_box2d_post_step);
|
||||
f(vm, self->GetBodyList(), on_box2d_pre_step);
|
||||
self->Step(dt, velocity_iterations, position_iterations);
|
||||
f(vm, self->GetBodyList(), on_box2d_post_step);
|
||||
return vm->None;
|
||||
});
|
||||
|
||||
// raycast
|
||||
vm->bind(type, "raycast(self, start: vec2, end: vec2) -> list[Body]", [](VM* vm, ArgsView args){
|
||||
auto _lock = vm->heap.gc_scope_lock();
|
||||
PyWorld& self = _CAST(PyWorld&, args[0]);
|
||||
b2Vec2 start = CAST(b2Vec2, args[1]);
|
||||
b2Vec2 end = CAST(b2Vec2, args[2]);
|
||||
MyRayCastCallback callback(vm);
|
||||
self->RayCast(&callback, start, end);
|
||||
return VAR(std::move(callback.result));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace pkpy
|
@ -68,15 +68,12 @@ class World:
|
||||
def ray_cast(self, start: vec2, end: vec2) -> list['Body']:
|
||||
"""raycast from start to end"""
|
||||
|
||||
def box_cast(self, p0: vec2, p1: vec2) -> list['Body']:
|
||||
def box_cast(self, lower: vec2, upper: vec2) -> list['Body']:
|
||||
"""query bodies in the AABB region."""
|
||||
|
||||
def step(self, dt: float, velocity_iterations: int, position_iterations: int) -> None:
|
||||
"""step the simulation, e.g. world.step(1/60, 8, 3)"""
|
||||
|
||||
def destroy(self):
|
||||
"""destroy this world."""
|
||||
|
||||
# enum
|
||||
# {
|
||||
# e_shapeBit = 0x0001, ///< draw shapes
|
||||
@ -93,13 +90,16 @@ class World:
|
||||
|
||||
class Body:
|
||||
type: int # 0-static, 1-kinematic, 2-dynamic, by default 2
|
||||
mass: float
|
||||
inertia: float
|
||||
gravity_scale: float
|
||||
fixed_rotation: bool
|
||||
enabled: bool
|
||||
bullet: bool # whether to use continuous collision detection
|
||||
|
||||
@property
|
||||
def mass(self) -> float: ...
|
||||
@property
|
||||
def inertia(self) -> float: ...
|
||||
|
||||
position: vec2
|
||||
rotation: float # in radians (counter-clockwise)
|
||||
velocity: vec2 # linear velocity
|
||||
@ -125,8 +125,8 @@ class Body:
|
||||
def apply_force(self, force: vec2, point: vec2): ...
|
||||
def apply_force_to_center(self, force: vec2): ...
|
||||
def apply_torque(self, torque: float): ...
|
||||
def apply_linear_impulse(self, impulse: vec2, point: vec2): ...
|
||||
def apply_linear_impulse_to_center(self, impulse: vec2): ...
|
||||
def apply_impulse(self, impulse: vec2, point: vec2): ...
|
||||
def apply_impulse_to_center(self, impulse: vec2): ...
|
||||
def apply_angular_impulse(self, impulse: float): ...
|
||||
|
||||
def get_node(self) -> _NodeLike:
|
||||
|
@ -67,7 +67,7 @@ a property is a python's `property` that attached to a type instance with a gett
|
||||
|
||||
You can use `@property` to create python property or use `vm->property` to create native property.
|
||||
|
||||
You can also use `vm->bind_property()`, the new style property binding function.
|
||||
Use `vm->bind_property()`, the new style property binding function.
|
||||
|
||||
```cpp
|
||||
struct Point {
|
||||
@ -86,7 +86,8 @@ struct Point {
|
||||
});
|
||||
|
||||
// getter and setter of property `x`
|
||||
type->attr().set("x", vm->property([](VM* vm, ArgsView args){
|
||||
vm->bind_property(type, "x: int",
|
||||
[](VM* vm, ArgsView args){
|
||||
Point& self = CAST(Point&, args[0]);
|
||||
return VAR(self.x);
|
||||
},
|
||||
@ -94,7 +95,7 @@ struct Point {
|
||||
Point& self = CAST(Point&, args[0]);
|
||||
self.x = CAST(int, args[1]);
|
||||
return vm->None;
|
||||
}));
|
||||
});
|
||||
}
|
||||
};
|
||||
```
|
||||
|
@ -65,33 +65,6 @@ struct NativeProxyMethodC final: NativeProxyFuncCBase {
|
||||
}
|
||||
};
|
||||
|
||||
template<typename _OpaqueT, typename Ret, typename T, typename... Params>
|
||||
struct NativeProxyOpaqueMethodC final: NativeProxyFuncCBase {
|
||||
static_assert(std::is_base_of_v<OpaquePointer<T>, _OpaqueT>);
|
||||
static constexpr int N = sizeof...(Params);
|
||||
using _Fp = Ret(T::*)(Params...);
|
||||
_Fp func;
|
||||
NativeProxyOpaqueMethodC(_Fp func) : func(func) {}
|
||||
|
||||
PyObject* operator()(VM* vm, ArgsView args) override {
|
||||
PK_ASSERT(args.size() == N+1);
|
||||
return call<Ret>(vm, args, std::make_index_sequence<N>());
|
||||
}
|
||||
|
||||
template<typename __Ret, size_t... Is>
|
||||
PyObject* call(VM* vm, ArgsView args, std::index_sequence<Is...>){
|
||||
OpaquePointer<T>& _opa_self = py_cast<_OpaqueT&>(vm, args[0]);
|
||||
T& self = *_opa_self.ptr;
|
||||
if constexpr(std::is_void_v<__Ret>){
|
||||
(self.*func)(py_cast<Params>(vm, args[Is+1])...);
|
||||
return vm->None;
|
||||
}else{
|
||||
__Ret ret = (self.*func)(py_cast<Params>(vm, args[Is+1])...);
|
||||
return VAR(std::move(ret));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
inline PyObject* proxy_wrapper(VM* vm, ArgsView args){
|
||||
NativeProxyFuncCBase* pf = lambda_get_userdata<NativeProxyFuncCBase*>(args.begin());
|
||||
return (*pf)(vm, args);
|
||||
@ -108,56 +81,44 @@ void _bind(VM* vm, PyObject* obj, const char* sig, Ret(T::*func)(Params...)){
|
||||
auto proxy = new NativeProxyMethodC<Ret, T, Params...>(func);
|
||||
vm->bind(obj, sig, proxy_wrapper, proxy);
|
||||
}
|
||||
|
||||
template<typename _OpaqueT, typename Ret, typename T, typename... Params>
|
||||
void _bind_opaque(VM* vm, PyObject* obj, const char* sig, Ret(T::*func)(Params...)){
|
||||
auto proxy = new NativeProxyOpaqueMethodC<_OpaqueT, Ret, T, Params...>(func);
|
||||
vm->bind(obj, sig, proxy_wrapper, proxy);
|
||||
}
|
||||
/*****************************************************************/
|
||||
#define PK_REGISTER_FIELD(T, NAME) \
|
||||
type->attr().set(#NAME, vm->property( \
|
||||
#define PK_REGISTER_FIELD(T, NAME, REF, EXPR) \
|
||||
vm->bind_property(type, NAME, \
|
||||
[](VM* vm, ArgsView args){ \
|
||||
T& self = _CAST(T&, args[0]); \
|
||||
return VAR(self->NAME); \
|
||||
return VAR(self.REF().EXPR); \
|
||||
}, \
|
||||
[](VM* vm, ArgsView args){ \
|
||||
T& self = _CAST(T&, args[0]); \
|
||||
self->NAME = CAST(decltype(self->NAME), args[1]); \
|
||||
self.REF().EXPR = CAST(decltype(self.REF().EXPR), args[1]); \
|
||||
return vm->None; \
|
||||
}));
|
||||
});
|
||||
|
||||
#define PK_REGISTER_READONLY_FIELD(T, NAME) \
|
||||
type->attr().set(#NAME, vm->property( \
|
||||
#define PK_REGISTER_READONLY_FIELD(T, NAME, REF, EXPR) \
|
||||
vm->bind_property(type, NAME, \
|
||||
[](VM* vm, ArgsView args){ \
|
||||
T& self = _CAST(T&, args[0]); \
|
||||
return VAR(self->NAME); \
|
||||
}));
|
||||
return VAR(self.REF().EXPR); \
|
||||
});
|
||||
|
||||
#define PK_REGISTER_PROPERTY(T, NAME, __tp) \
|
||||
type->attr().set(#NAME, vm->property( \
|
||||
#define PK_REGISTER_PROPERTY(T, NAME, REF, FGET, FSET) \
|
||||
vm->bind_property(type, NAME, \
|
||||
[](VM* vm, ArgsView args){ \
|
||||
T& self = _CAST(T&, args[0]); \
|
||||
return VAR(self->get_##NAME()); \
|
||||
return VAR(self.REF().FGET()); \
|
||||
}, \
|
||||
[](VM* vm, ArgsView args){ \
|
||||
T& self = _CAST(T&, args[0]); \
|
||||
using __NT = decltype(self->get_##NAME()); \
|
||||
self->set_##NAME(CAST(__NT, args[1])); \
|
||||
using __NT = decltype(self.REF().FGET()); \
|
||||
self.REF().FSET(CAST(__NT, args[1])); \
|
||||
return vm->None; \
|
||||
}, __tp));
|
||||
});
|
||||
|
||||
#define PK_REGISTER_READONLY_PROPERTY(T, NAME, __tp) \
|
||||
type->attr().set(#NAME, vm->property( \
|
||||
#define PK_REGISTER_READONLY_PROPERTY(T, NAME, REF, FGET) \
|
||||
vm->bind_property(type, NAME, \
|
||||
[](VM* vm, ArgsView args){ \
|
||||
T& self = _CAST(T&, args[0]); \
|
||||
return VAR(self->get_##NAME()); \
|
||||
}, nullptr, __tp));
|
||||
|
||||
#define PK_REGISTER_CONSTRUCTOR(T, T0) \
|
||||
vm->bind_constructor<2>(type, [](VM* vm, ArgsView args){ \
|
||||
void* p = CAST(void*, args[0]); \
|
||||
return VAR_T(T, (T0*)p); \
|
||||
return VAR(self.REF().FGET()); \
|
||||
});
|
||||
|
||||
} // namespace pkpy
|
@ -27,7 +27,7 @@ namespace pkpy {
|
||||
return type; \
|
||||
}
|
||||
|
||||
#define VAR_T(T, ...) vm->heap.gcnew<T>(T::_type(vm), T(__VA_ARGS__))
|
||||
#define VAR_T(T, ...) vm->heap.gcnew<T>(T::_type(vm), __VA_ARGS__)
|
||||
|
||||
int c99_sizeof(VM*, const Str&);
|
||||
|
||||
|
@ -129,7 +129,7 @@ struct FuncDecl {
|
||||
};
|
||||
|
||||
struct UserData{
|
||||
char data[16];
|
||||
char data[15];
|
||||
bool empty;
|
||||
|
||||
UserData(): empty(true) {}
|
||||
@ -186,7 +186,8 @@ struct Function{
|
||||
template<>
|
||||
struct Py_<Function> final: PyObject {
|
||||
Function _value;
|
||||
Py_(Type type, Function val): PyObject(type), _value(val) {
|
||||
template<typename... Args>
|
||||
Py_(Type type, Args&&... args): PyObject(type), _value(std::forward<Args>(args)...) {
|
||||
enable_instance_dict();
|
||||
}
|
||||
void _obj_gc_mark() override {
|
||||
@ -199,7 +200,8 @@ struct Py_<Function> final: PyObject {
|
||||
template<>
|
||||
struct Py_<NativeFunc> final: PyObject {
|
||||
NativeFunc _value;
|
||||
Py_(Type type, NativeFunc val): PyObject(type), _value(val) {
|
||||
template<typename... Args>
|
||||
Py_(Type type, Args&&... args): PyObject(type), _value(std::forward<Args>(args)...) {
|
||||
enable_instance_dict();
|
||||
}
|
||||
void _obj_gc_mark() override {
|
||||
|
@ -21,7 +21,7 @@
|
||||
#include <type_traits>
|
||||
#include <random>
|
||||
|
||||
#define PK_VERSION "1.1.2"
|
||||
#define PK_VERSION "1.1.3"
|
||||
|
||||
#include "config.h"
|
||||
#include "export.h"
|
||||
@ -164,4 +164,10 @@ struct is_pod {
|
||||
static constexpr bool value = std::is_trivially_copyable_v<T> && std::is_standard_layout_v<T>;
|
||||
};
|
||||
|
||||
#define PK_ALWAYS_PASS_BY_POINTER(T) \
|
||||
T(const T&) = delete; \
|
||||
T& operator=(const T&) = delete; \
|
||||
T(T&&) = delete; \
|
||||
T& operator=(T&&) = delete;
|
||||
|
||||
} // namespace pkpy
|
||||
|
@ -37,29 +37,21 @@ struct ManagedHeap{
|
||||
}
|
||||
/********************/
|
||||
|
||||
template<typename T>
|
||||
PyObject* gcnew(Type type, T&& val){
|
||||
template<typename T, typename... Args>
|
||||
PyObject* gcnew(Type type, Args&&... args){
|
||||
using __T = Py_<std::decay_t<T>>;
|
||||
#if _WIN32
|
||||
// https://github.com/blueloveTH/pocketpy/issues/94#issuecomment-1594784476
|
||||
PyObject* obj = new(pool64.alloc<__T>()) Py_<std::decay_t<T>>(type, std::forward<T>(val));
|
||||
#else
|
||||
PyObject* obj = new(pool64.alloc<__T>()) __T(type, std::forward<T>(val));
|
||||
#endif
|
||||
PyObject* obj = new(pool64.alloc<__T>()) Py_<std::decay_t<T>>(type, std::forward<Args>(args)...);
|
||||
gen.push_back(obj);
|
||||
gc_counter++;
|
||||
return obj;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
PyObject* _new(Type type, T&& val){
|
||||
template<typename T, typename... Args>
|
||||
PyObject* _new(Type type, Args&&... args){
|
||||
using __T = Py_<std::decay_t<T>>;
|
||||
#if _WIN32
|
||||
// https://github.com/blueloveTH/pocketpy/issues/94#issuecomment-1594784476
|
||||
PyObject* obj = new(pool64.alloc<__T>()) Py_<std::decay_t<T>>(type, std::forward<T>(val));
|
||||
#else
|
||||
PyObject* obj = new(pool64.alloc<__T>()) __T(type, std::forward<T>(val));
|
||||
#endif
|
||||
PyObject* obj = new(pool64.alloc<__T>()) Py_<std::decay_t<T>>(type, std::forward<Args>(args)...);
|
||||
obj->gc.enabled = false;
|
||||
_no_gc.push_back(obj);
|
||||
return obj;
|
||||
|
@ -31,8 +31,8 @@ struct BoundMethod {
|
||||
struct Property{
|
||||
PyObject* getter;
|
||||
PyObject* setter;
|
||||
const char* type_hint;
|
||||
Property(PyObject* getter, PyObject* setter, const char* type_hint) : getter(getter), setter(setter), type_hint(type_hint) {}
|
||||
Str type_hint;
|
||||
Property(PyObject* getter, PyObject* setter, Str type_hint) : getter(getter), setter(setter), type_hint(type_hint) {}
|
||||
};
|
||||
|
||||
struct Range {
|
||||
@ -134,8 +134,9 @@ struct Py_ final: PyObject {
|
||||
_value._gc_mark();
|
||||
}
|
||||
}
|
||||
Py_(Type type, const T& value) : PyObject(type), _value(value) {}
|
||||
Py_(Type type, T&& value) : PyObject(type), _value(std::move(value)) {}
|
||||
|
||||
template <typename... Args>
|
||||
Py_(Type type, Args&&... args) : PyObject(type), _value(std::forward<Args>(args)...) { }
|
||||
};
|
||||
|
||||
struct MappingProxy{
|
||||
@ -320,7 +321,8 @@ struct Py_<Slice> final: PyObject {
|
||||
template<>
|
||||
struct Py_<Super> final: PyObject {
|
||||
Super _value;
|
||||
Py_(Type type, Super val): PyObject(type), _value(val) {}
|
||||
template<typename... Args>
|
||||
Py_(Type type, Args&&... args): PyObject(type), _value(std::forward<Args>(args)...) {}
|
||||
void _obj_gc_mark() override {
|
||||
PK_OBJ_MARK(_value.first);
|
||||
}
|
||||
@ -328,8 +330,7 @@ struct Py_<Super> final: PyObject {
|
||||
|
||||
template<>
|
||||
struct Py_<DummyInstance> final: PyObject {
|
||||
Py_(Type type, DummyInstance val): PyObject(type) {
|
||||
PK_UNUSED(val);
|
||||
Py_(Type type): PyObject(type) {
|
||||
enable_instance_dict();
|
||||
}
|
||||
void _obj_gc_mark() override {}
|
||||
@ -346,8 +347,7 @@ struct Py_<Type> final: PyObject {
|
||||
|
||||
template<>
|
||||
struct Py_<DummyModule> final: PyObject {
|
||||
Py_(Type type, DummyModule val): PyObject(type) {
|
||||
PK_UNUSED(val);
|
||||
Py_(Type type): PyObject(type) {
|
||||
enable_instance_dict(kTypeAttrLoadFactor);
|
||||
}
|
||||
void _obj_gc_mark() override {}
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "common.h"
|
||||
#include "memory.h"
|
||||
#include "vector.h"
|
||||
#include <cstddef>
|
||||
|
||||
namespace pkpy {
|
||||
|
||||
@ -22,6 +23,7 @@ struct Str{
|
||||
Str(int size, bool is_ascii);
|
||||
Str(const std::string& s);
|
||||
Str(std::string_view s);
|
||||
Str(std::nullptr_t) { FATAL_ERROR(); }
|
||||
Str(const char* s);
|
||||
Str(const char* s, int len);
|
||||
Str(const Str& other);
|
||||
|
@ -42,8 +42,8 @@ namespace pkpy{
|
||||
PK_UNUSED(vm); \
|
||||
return PK_OBJ_GET(ctype, obj); \
|
||||
} \
|
||||
inline PyObject* py_var(VM* vm, const ctype& value) { return vm->heap.gcnew(vm->ptype, value);} \
|
||||
inline PyObject* py_var(VM* vm, ctype&& value) { return vm->heap.gcnew(vm->ptype, std::move(value));}
|
||||
inline PyObject* py_var(VM* vm, const ctype& value) { return vm->heap.gcnew<ctype>(vm->ptype, value);} \
|
||||
inline PyObject* py_var(VM* vm, ctype&& value) { return vm->heap.gcnew<ctype>(vm->ptype, std::move(value));}
|
||||
|
||||
|
||||
typedef PyObject* (*BinaryFuncC)(VM*, PyObject*, PyObject*);
|
||||
@ -107,6 +107,8 @@ struct FrameId{
|
||||
typedef void(*PrintFunc)(VM*, const Str&);
|
||||
|
||||
class VM {
|
||||
PK_ALWAYS_PASS_BY_POINTER(VM)
|
||||
|
||||
VM* vm; // self reference for simplify code
|
||||
public:
|
||||
ManagedHeap heap;
|
||||
@ -201,7 +203,6 @@ public:
|
||||
return call_method(self, callable, args...);
|
||||
}
|
||||
|
||||
PyObject* property(NativeFuncC fget, NativeFuncC fset=nullptr, const char* type_hint=nullptr);
|
||||
PyObject* new_type_object(PyObject* mod, StrName name, Type base, bool subclass_enabled=true);
|
||||
Type _new_type_object(StrName name, Type base=0);
|
||||
PyObject* _find_type_object(const Str& type);
|
||||
@ -460,7 +461,7 @@ public:
|
||||
// new style binding api
|
||||
PyObject* bind(PyObject*, const char*, const char*, NativeFuncC, UserData userdata={});
|
||||
PyObject* bind(PyObject*, const char*, NativeFuncC, UserData userdata={});
|
||||
PyObject* bind_property(PyObject*, StrName name, const char* type_hint, NativeFuncC fget, NativeFuncC fset=nullptr);
|
||||
PyObject* bind_property(PyObject*, Str, NativeFuncC fget, NativeFuncC fset=nullptr);
|
||||
};
|
||||
|
||||
DEF_NATIVE_2(Str, tp_str)
|
||||
|
@ -99,7 +99,7 @@ void add_module_io(VM* vm){
|
||||
void add_module_os(VM* vm){
|
||||
#if PK_ENABLE_OS
|
||||
PyObject* mod = vm->new_module("os");
|
||||
PyObject* path_obj = vm->heap.gcnew<DummyInstance>(vm->tp_object, {});
|
||||
PyObject* path_obj = vm->heap.gcnew<DummyInstance>(vm->tp_object);
|
||||
mod->attr().set("path", path_obj);
|
||||
|
||||
// Working directory is shared by all VMs!!
|
||||
|
@ -36,14 +36,15 @@ namespace pkpy{
|
||||
});
|
||||
|
||||
#define BIND_VEC_FIELD(D, name) \
|
||||
type->attr().set(#name, vm->property([](VM* vm, ArgsView args){ \
|
||||
vm->bind_property(type, #name, \
|
||||
[](VM* vm, ArgsView args){ \
|
||||
PyVec##D& self = _CAST(PyVec##D&, args[0]); \
|
||||
return VAR(self.name); \
|
||||
}, [](VM* vm, ArgsView args){ \
|
||||
PyVec##D& self = _CAST(PyVec##D&, args[0]); \
|
||||
self.name = CAST(f64, args[1]); \
|
||||
return vm->None; \
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
void PyVec2::_register(VM* vm, PyObject* mod, PyObject* type){
|
||||
@ -284,14 +285,15 @@ namespace pkpy{
|
||||
});
|
||||
|
||||
#define PROPERTY_FIELD(field) \
|
||||
type->attr().set(#field, vm->property([](VM* vm, ArgsView args){ \
|
||||
vm->bind_property(type, #field ": float", \
|
||||
[](VM* vm, ArgsView args){ \
|
||||
PyMat3x3& self = _CAST(PyMat3x3&, args[0]); \
|
||||
return VAR(self.field); \
|
||||
}, [](VM* vm, ArgsView args){ \
|
||||
PyMat3x3& self = _CAST(PyMat3x3&, args[0]); \
|
||||
self.field = CAST(f64, args[1]); \
|
||||
return vm->None; \
|
||||
}));
|
||||
});
|
||||
|
||||
PROPERTY_FIELD(_11)
|
||||
PROPERTY_FIELD(_12)
|
||||
|
114
src/pocketpy.cpp
114
src/pocketpy.cpp
@ -113,7 +113,7 @@ void init_builtins(VM* _vm) {
|
||||
vm->TypeError("super(): " + _0.escape() + " is not an instance of " + _1.escape());
|
||||
}
|
||||
Type base = vm->_all_types[type].base;
|
||||
return vm->heap.gcnew(vm->tp_super, Super(args[1], base));
|
||||
return vm->heap.gcnew<Super>(vm->tp_super, args[1], base);
|
||||
});
|
||||
|
||||
_vm->bind_builtin_func<2>("isinstance", [](VM* vm, ArgsView args) {
|
||||
@ -149,36 +149,6 @@ void init_builtins(VM* _vm) {
|
||||
}
|
||||
});
|
||||
|
||||
_vm->bind_builtin_func<3>("pow", [](VM* vm, ArgsView args) {
|
||||
i64 lhs = CAST(i64, args[0]); // assume lhs>=0
|
||||
i64 rhs = CAST(i64, args[1]); // assume rhs>=0
|
||||
i64 mod = CAST(i64, args[2]); // assume mod>0, mod*mod should not overflow
|
||||
|
||||
if(rhs <= 0){
|
||||
vm->ValueError("pow(): rhs should be positive");
|
||||
}
|
||||
|
||||
PK_LOCAL_STATIC const auto _mul = [](i64 a, i64 b, i64 c){
|
||||
if(c < 16384) return (a%c) * (b%c) % c;
|
||||
i64 res = 0;
|
||||
while(b > 0){
|
||||
if(b & 1) res = (res + a) % c;
|
||||
a = (a << 1) % c;
|
||||
b >>= 1;
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
i64 res = 1;
|
||||
lhs %= mod;
|
||||
while(rhs){
|
||||
if(rhs & 1) res = _mul(res, lhs, mod);
|
||||
lhs = _mul(lhs, lhs, mod);
|
||||
rhs >>= 1;
|
||||
}
|
||||
return VAR(res);
|
||||
});
|
||||
|
||||
_vm->bind_builtin_func<1>("id", [](VM* vm, ArgsView args) {
|
||||
PyObject* obj = args[0];
|
||||
if(is_tagged(obj)) return vm->None;
|
||||
@ -340,7 +310,7 @@ void init_builtins(VM* _vm) {
|
||||
_vm->cached_object__new__ = _vm->bind_constructor<1>("object", [](VM* vm, ArgsView args) {
|
||||
vm->check_non_tagged_type(args[0], vm->tp_type);
|
||||
Type t = PK_OBJ_GET(Type, args[0]);
|
||||
return vm->heap.gcnew<DummyInstance>(t, {});
|
||||
return vm->heap.gcnew<DummyInstance>(t);
|
||||
});
|
||||
|
||||
_vm->bind_constructor<2>("type", PK_LAMBDA(vm->_t(args[1])));
|
||||
@ -1018,6 +988,11 @@ void init_builtins(VM* _vm) {
|
||||
return (i64)_CAST(MappingProxy&, obj).attr().size();
|
||||
});
|
||||
|
||||
_vm->bind__hash__(_vm->tp_mappingproxy, [](VM* vm, PyObject* obj) {
|
||||
vm->TypeError("unhashable type: 'mappingproxy'");
|
||||
return (i64)0;
|
||||
});
|
||||
|
||||
_vm->bind__getitem__(_vm->tp_mappingproxy, [](VM* vm, PyObject* obj, PyObject* index) {
|
||||
MappingProxy& self = _CAST(MappingProxy&, obj);
|
||||
StrName key = CAST(Str&, index);
|
||||
@ -1074,6 +1049,11 @@ void init_builtins(VM* _vm) {
|
||||
return (i64)_CAST(Dict&, obj).size();
|
||||
});
|
||||
|
||||
_vm->bind__hash__(_vm->tp_dict, [](VM* vm, PyObject* obj) {
|
||||
vm->TypeError("unhashable type: 'dict'");
|
||||
return (i64)0;
|
||||
});
|
||||
|
||||
_vm->bind__getitem__(_vm->tp_dict, [](VM* vm, PyObject* obj, PyObject* index) {
|
||||
Dict& self = _CAST(Dict&, obj);
|
||||
PyObject* ret = self.try_get(index);
|
||||
@ -1216,41 +1196,41 @@ void init_builtins(VM* _vm) {
|
||||
/************ property ************/
|
||||
_vm->bind_constructor<-1>("property", [](VM* vm, ArgsView args) {
|
||||
if(args.size() == 1+1){
|
||||
return VAR(Property(args[1], vm->None, nullptr));
|
||||
return VAR(Property(args[1], vm->None, ""));
|
||||
}else if(args.size() == 1+2){
|
||||
return VAR(Property(args[1], args[2], nullptr));
|
||||
return VAR(Property(args[1], args[2], ""));
|
||||
}
|
||||
vm->TypeError("property() takes at most 2 arguments");
|
||||
return vm->None;
|
||||
});
|
||||
|
||||
_vm->bind_property(_vm->_t(_vm->tp_property), "type_hint", "str", [](VM* vm, ArgsView args){
|
||||
_vm->bind_property(_vm->_t(_vm->tp_property), "type_hint: str", [](VM* vm, ArgsView args){
|
||||
Property& self = _CAST(Property&, args[0]);
|
||||
if(self.type_hint == nullptr) return vm->None;
|
||||
return VAR(self.type_hint);
|
||||
});
|
||||
|
||||
_vm->_t(_vm->tp_function)->attr().set("__doc__", _vm->property([](VM* vm, ArgsView args) {
|
||||
|
||||
_vm->bind_property(_vm->_t(_vm->tp_function), "__doc__", [](VM* vm, ArgsView args) {
|
||||
Function& func = _CAST(Function&, args[0]);
|
||||
return VAR(func.decl->docstring);
|
||||
}));
|
||||
});
|
||||
|
||||
_vm->_t(_vm->tp_native_func)->attr().set("__doc__", _vm->property([](VM* vm, ArgsView args) {
|
||||
_vm->bind_property(_vm->_t(_vm->tp_native_func), "__doc__", [](VM* vm, ArgsView args) {
|
||||
NativeFunc& func = _CAST(NativeFunc&, args[0]);
|
||||
if(func.decl != nullptr) return VAR(func.decl->docstring);
|
||||
return VAR("");
|
||||
}));
|
||||
});
|
||||
|
||||
_vm->_t(_vm->tp_function)->attr().set("__signature__", _vm->property([](VM* vm, ArgsView args) {
|
||||
_vm->bind_property(_vm->_t(_vm->tp_function), "__signature__", [](VM* vm, ArgsView args) {
|
||||
Function& func = _CAST(Function&, args[0]);
|
||||
return VAR(func.decl->signature);
|
||||
}));
|
||||
});
|
||||
|
||||
_vm->_t(_vm->tp_native_func)->attr().set("__signature__", _vm->property([](VM* vm, ArgsView args) {
|
||||
_vm->bind_property(_vm->_t(_vm->tp_native_func), "__signature__", [](VM* vm, ArgsView args) {
|
||||
NativeFunc& func = _CAST(NativeFunc&, args[0]);
|
||||
if(func.decl != nullptr) return VAR(func.decl->signature);
|
||||
return VAR("unknown(*args, **kwargs)");
|
||||
}));
|
||||
});
|
||||
|
||||
RangeIter::register_class(_vm, _vm->builtins);
|
||||
ArrayIter::register_class(_vm, _vm->builtins);
|
||||
@ -1364,8 +1344,8 @@ void add_module_sys(VM* vm){
|
||||
vm->setattr(mod, "version", VAR(PK_VERSION));
|
||||
vm->setattr(mod, "platform", VAR(PK_SYS_PLATFORM));
|
||||
|
||||
PyObject* stdout_ = vm->heap.gcnew<DummyInstance>(vm->tp_object, {});
|
||||
PyObject* stderr_ = vm->heap.gcnew<DummyInstance>(vm->tp_object, {});
|
||||
PyObject* stdout_ = vm->heap.gcnew<DummyInstance>(vm->tp_object);
|
||||
PyObject* stderr_ = vm->heap.gcnew<DummyInstance>(vm->tp_object);
|
||||
vm->setattr(mod, "stdout", stdout_);
|
||||
vm->setattr(mod, "stderr", stderr_);
|
||||
|
||||
@ -1520,41 +1500,41 @@ void add_module_gc(VM* vm){
|
||||
void VM::post_init(){
|
||||
init_builtins(this);
|
||||
|
||||
_t(tp_object)->attr().set("__class__", property(PK_LAMBDA(vm->_t(args[0]))));
|
||||
_t(tp_type)->attr().set("__base__", property([](VM* vm, ArgsView args){
|
||||
bind_property(_t(tp_object), "__class__", PK_LAMBDA(VAR(vm->_t(args[0]))));
|
||||
bind_property(_t(tp_type), "__base__", [](VM* vm, ArgsView args){
|
||||
const PyTypeInfo& info = vm->_all_types[PK_OBJ_GET(Type, args[0])];
|
||||
return info.base.index == -1 ? vm->None : vm->_all_types[info.base].obj;
|
||||
}));
|
||||
_t(tp_type)->attr().set("__name__", property([](VM* vm, ArgsView args){
|
||||
});
|
||||
bind_property(_t(tp_type), "__name__", [](VM* vm, ArgsView args){
|
||||
const PyTypeInfo& info = vm->_all_types[PK_OBJ_GET(Type, args[0])];
|
||||
return VAR(info.name);
|
||||
}));
|
||||
|
||||
_t(tp_bound_method)->attr().set("__self__", property([](VM* vm, ArgsView args){
|
||||
});
|
||||
bind_property(_t(tp_bound_method), "__self__", [](VM* vm, ArgsView args){
|
||||
return CAST(BoundMethod&, args[0]).self;
|
||||
}));
|
||||
_t(tp_bound_method)->attr().set("__func__", property([](VM* vm, ArgsView args){
|
||||
});
|
||||
bind_property(_t(tp_bound_method), "__func__", [](VM* vm, ArgsView args){
|
||||
return CAST(BoundMethod&, args[0]).func;
|
||||
}));
|
||||
});
|
||||
|
||||
bind__eq__(tp_bound_method, [](VM* vm, PyObject* lhs, PyObject* rhs){
|
||||
if(!is_non_tagged_type(rhs, vm->tp_bound_method)) return vm->NotImplemented;
|
||||
return VAR(_CAST(BoundMethod&, lhs) == _CAST(BoundMethod&, rhs));
|
||||
});
|
||||
_t(tp_slice)->attr().set("start", property([](VM* vm, ArgsView args){
|
||||
return CAST(Slice&, args[0]).start;
|
||||
}));
|
||||
_t(tp_slice)->attr().set("stop", property([](VM* vm, ArgsView args){
|
||||
return CAST(Slice&, args[0]).stop;
|
||||
}));
|
||||
_t(tp_slice)->attr().set("step", property([](VM* vm, ArgsView args){
|
||||
return CAST(Slice&, args[0]).step;
|
||||
}));
|
||||
|
||||
_t(tp_object)->attr().set("__dict__", property([](VM* vm, ArgsView args){
|
||||
bind_property(_t(tp_slice), "start", [](VM* vm, ArgsView args){
|
||||
return CAST(Slice&, args[0]).start;
|
||||
});
|
||||
bind_property(_t(tp_slice), "stop", [](VM* vm, ArgsView args){
|
||||
return CAST(Slice&, args[0]).stop;
|
||||
});
|
||||
bind_property(_t(tp_slice), "step", [](VM* vm, ArgsView args){
|
||||
return CAST(Slice&, args[0]).step;
|
||||
});
|
||||
|
||||
bind_property(_t(tp_object), "__dict__", [](VM* vm, ArgsView args){
|
||||
if(is_tagged(args[0]) || !args[0]->is_attr_valid()) return vm->None;
|
||||
return VAR(MappingProxy(args[0]));
|
||||
}));
|
||||
});
|
||||
|
||||
#if !PK_DEBUG_NO_BUILTINS
|
||||
add_module_sys(this);
|
||||
|
36
src/vm.cpp
36
src/vm.cpp
@ -113,13 +113,6 @@ namespace pkpy{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PyObject* VM::property(NativeFuncC fget, NativeFuncC fset, const char* type_hint){
|
||||
PyObject* _0 = heap.gcnew(tp_native_func, NativeFunc(fget, 1, false));
|
||||
PyObject* _1 = vm->None;
|
||||
if(fset != nullptr) _1 = heap.gcnew(tp_native_func, NativeFunc(fset, 2, false));
|
||||
return VAR(Property(_0, _1, type_hint));
|
||||
}
|
||||
|
||||
PyObject* VM::new_type_object(PyObject* mod, StrName name, Type base, bool subclass_enabled){
|
||||
PyObject* obj = heap._new<Type>(tp_type, _all_types.size());
|
||||
const PyTypeInfo& base_info = _all_types[base];
|
||||
@ -434,7 +427,7 @@ PyObject* VM::format(Str spec, PyObject* obj){
|
||||
}
|
||||
|
||||
PyObject* VM::new_module(StrName name) {
|
||||
PyObject* obj = heap._new<DummyModule>(tp_module, DummyModule());
|
||||
PyObject* obj = heap._new<DummyModule>(tp_module);
|
||||
obj->attr().set("__name__", VAR(name.sv()));
|
||||
// we do not allow override in order to avoid memory leak
|
||||
// it is because Module objects are not garbage collected
|
||||
@ -592,12 +585,12 @@ void VM::init_builtin_types(){
|
||||
tp_property = _new_type_object("property");
|
||||
tp_star_wrapper = _new_type_object("_star_wrapper");
|
||||
|
||||
this->None = heap._new<Dummy>(_new_type_object("NoneType"), {});
|
||||
this->NotImplemented = heap._new<Dummy>(_new_type_object("NotImplementedType"), {});
|
||||
this->Ellipsis = heap._new<Dummy>(_new_type_object("ellipsis"), {});
|
||||
this->True = heap._new<Dummy>(tp_bool, {});
|
||||
this->False = heap._new<Dummy>(tp_bool, {});
|
||||
this->StopIteration = heap._new<Dummy>(_new_type_object("StopIterationType"), {});
|
||||
this->None = heap._new<Dummy>(_new_type_object("NoneType"));
|
||||
this->NotImplemented = heap._new<Dummy>(_new_type_object("NotImplementedType"));
|
||||
this->Ellipsis = heap._new<Dummy>(_new_type_object("ellipsis"));
|
||||
this->True = heap._new<Dummy>(tp_bool);
|
||||
this->False = heap._new<Dummy>(tp_bool);
|
||||
this->StopIteration = heap._new<Dummy>(_new_type_object("StopIterationType"));
|
||||
|
||||
this->builtins = new_module("builtins");
|
||||
|
||||
@ -804,7 +797,7 @@ PyObject* VM::vectorcall(int ARGC, int KWARGC, bool op_call){
|
||||
if(new_f == cached_object__new__) {
|
||||
// fast path for object.__new__
|
||||
Type t = PK_OBJ_GET(Type, callable);
|
||||
obj= vm->heap.gcnew<DummyInstance>(t, {});
|
||||
obj= vm->heap.gcnew<DummyInstance>(t);
|
||||
}else{
|
||||
PUSH(new_f);
|
||||
PUSH(PY_NULL);
|
||||
@ -979,8 +972,17 @@ PyObject* VM::bind(PyObject* obj, const char* sig, const char* docstring, Native
|
||||
return f_obj;
|
||||
}
|
||||
|
||||
PyObject* VM::bind_property(PyObject* obj, StrName name, const char* type_hint, NativeFuncC fget, NativeFuncC fset){
|
||||
PyObject* prop = property(fget, fset, type_hint);
|
||||
PyObject* VM::bind_property(PyObject* obj, Str name, NativeFuncC fget, NativeFuncC fset){
|
||||
PyObject* _0 = heap.gcnew<NativeFunc>(tp_native_func, fget, 1, false);
|
||||
PyObject* _1 = vm->None;
|
||||
if(fset != nullptr) _1 = heap.gcnew<NativeFunc>(tp_native_func, fset, 2, false);
|
||||
Str type_hint;
|
||||
int pos = name.index(":");
|
||||
if(pos > 0){
|
||||
type_hint = name.substr(pos + 1).strip();
|
||||
name = name.substr(0, pos).strip();
|
||||
}
|
||||
PyObject* prop = VAR(Property(_0, _1, type_hint));
|
||||
obj->attr().set(name, prop);
|
||||
return prop;
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ if inq is not 1:
|
||||
if inq is not 0:
|
||||
assert False
|
||||
|
||||
assert pow(2,5000,2**59-1) == 17592186044416
|
||||
# assert pow(2,5000,2**59-1) == 17592186044416
|
||||
|
||||
def g(x):
|
||||
return x
|
||||
|
Loading…
x
Reference in New Issue
Block a user