Merge pull request #124 from blueloveTH/dev

Dev
This commit is contained in:
BLUELOVETH 2023-08-05 16:46:36 +08:00 committed by GitHub
commit 945407b3e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 492 additions and 635 deletions

View File

@ -1,5 +1,7 @@
name: build dylib
on: [push, pull_request]
on:
push:
branches: [ main ]
jobs:
build_win:

View File

@ -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}
)

View File

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

View 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

View 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

View File

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

View File

@ -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:

View File

@ -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,15 +86,16 @@ struct Point {
});
// getter and setter of property `x`
type->attr().set("x", vm->property([](VM* vm, ArgsView args){
Point& self = CAST(Point&, args[0]);
return VAR(self.x);
},
[](VM* vm, ArgsView args){
Point& self = CAST(Point&, args[0]);
self.x = CAST(int, args[1]);
return vm->None;
}));
vm->bind_property(type, "x: int",
[](VM* vm, ArgsView args){
Point& self = CAST(Point&, args[0]);
return VAR(self.x);
},
[](VM* vm, ArgsView args){
Point& self = CAST(Point&, args[0]);
self.x = CAST(int, args[1]);
return vm->None;
});
}
};
```

View File

@ -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( \
[](VM* vm, ArgsView args){ \
T& self = _CAST(T&, args[0]); \
return VAR(self->NAME); \
}, \
[](VM* vm, ArgsView args){ \
T& self = _CAST(T&, args[0]); \
self->NAME = CAST(decltype(self->NAME), args[1]); \
return vm->None; \
}));
#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.REF().EXPR); \
}, \
[](VM* vm, ArgsView args){ \
T& self = _CAST(T&, args[0]); \
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( \
[](VM* vm, ArgsView args){ \
T& self = _CAST(T&, args[0]); \
return VAR(self->NAME); \
}));
#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.REF().EXPR); \
});
#define PK_REGISTER_PROPERTY(T, NAME, __tp) \
type->attr().set(#NAME, vm->property( \
[](VM* vm, ArgsView args){ \
T& self = _CAST(T&, args[0]); \
return VAR(self->get_##NAME()); \
}, \
[](VM* vm, ArgsView args){ \
T& self = _CAST(T&, args[0]); \
using __NT = decltype(self->get_##NAME()); \
self->set_##NAME(CAST(__NT, args[1])); \
return vm->None; \
}, __tp));
#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.REF().FGET()); \
}, \
[](VM* vm, ArgsView args){ \
T& self = _CAST(T&, args[0]); \
using __NT = decltype(self.REF().FGET()); \
self.REF().FSET(CAST(__NT, args[1])); \
return vm->None; \
});
#define PK_REGISTER_READONLY_PROPERTY(T, NAME, __tp) \
type->attr().set(#NAME, vm->property( \
[](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); \
});
#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.REF().FGET()); \
});
} // namespace pkpy

View File

@ -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&);

View File

@ -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 {

View File

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

View File

@ -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;

View File

@ -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 {}

View File

@ -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);

View File

@ -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)

View File

@ -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!!

View File

@ -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)

View File

@ -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);
@ -1073,6 +1048,11 @@ void init_builtins(VM* _vm) {
_vm->bind__len__(_vm->tp_dict, [](VM* vm, PyObject* obj) {
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);
@ -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);

View File

@ -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;
}

View File

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