Browse Source

use new vector impl

blueloveTH 1 năm trước cách đây
mục cha
commit
a59592b799

+ 1 - 1
build_g.sh

@@ -1,5 +1,5 @@
 SRC=$(find src/ -name "*.cpp")
 
-FLAGS="-std=c++17 -O2 -stdlib=libc++ -Iinclude -frtti -Wfatal-errors -g"
+FLAGS="-std=c++17 -Og -stdlib=libc++ -Iinclude -frtti -Wfatal-errors -g"
 
 clang++ $FLAGS -o main src2/main.cpp $SRC

+ 4 - 4
include/pocketpy/codeobject.h

@@ -70,17 +70,17 @@ struct CodeObject {
     std::shared_ptr<SourceData> src;
     Str name;
 
-    std::vector<Bytecode> codes;
-    std::vector<LineInfo> lines;
+    vector<Bytecode> codes;
+    vector<LineInfo> lines;
     
     small_vector_2<PyVar, 8> consts;         // constants
     small_vector_2<StrName, 8> varnames;     // local variables
     int nlocals;                             // varnames.size()
 
     NameDictInt varnames_inv;
-    std::vector<CodeBlock> blocks;
+    vector<CodeBlock> blocks;
     NameDictInt labels;
-    std::vector<FuncDecl_> func_decls;
+    vector<FuncDecl_> func_decls;
 
     int start_line;
     int end_line;

+ 0 - 1
include/pocketpy/common.h

@@ -5,7 +5,6 @@
 #include <ctime>
 
 #include <stdexcept>
-#include <vector>
 #include <string>
 #include <chrono>
 #include <string_view>

+ 4 - 4
include/pocketpy/compiler.h

@@ -27,11 +27,11 @@ class Compiler {
     bool unknown_global_scope;     // for eval/exec() call
     // for parsing token stream
     int i = 0;
-    std::vector<Token> tokens;
+    vector<Token> tokens;
 
-    const Token& prev() const{ return tokens.at(i-1); }
-    const Token& curr() const{ return tokens.at(i); }
-    const Token& next() const{ return tokens.at(i+1); }
+    const Token& prev() const{ return tokens[i-1]; }
+    const Token& curr() const{ return tokens[i]; }
+    const Token& next() const{ return tokens[i+1]; }
     const Token& err() const{
         if(i >= tokens.size()) return prev();
         return curr();

+ 2 - 2
include/pocketpy/error.h

@@ -37,10 +37,10 @@ struct SourceData {
     CompileMode mode;
 
     Str source;
-    pod_vector<const char*> line_starts;
+    vector<const char*> line_starts;
 
     bool is_precompiled;
-    std::vector<Str> _precompiled_tokens;
+    vector<Str> _precompiled_tokens;
     
     SourceData(std::string_view source, const Str& filename, CompileMode mode);
     SourceData(const Str& filename, CompileMode mode);

+ 1 - 1
include/pocketpy/expr.h

@@ -352,7 +352,7 @@ struct CallExpr: Expr{
     Expr_ callable;
     Expr_vector args;
     // **a will be interpreted as a special keyword argument: {"**": a}
-    std::vector<std::pair<Str, Expr_>> kwargs;
+    vector<std::pair<Str, Expr_>> kwargs;
     void emit_(CodeEmitContext* ctx) override;
 };
 

+ 2 - 2
include/pocketpy/gc.h

@@ -8,8 +8,8 @@
 
 namespace pkpy {
 struct ManagedHeap{
-    std::vector<PyObject*> _no_gc;
-    std::vector<PyObject*> gen;
+    vector<PyObject*> _no_gc;
+    vector<PyObject*> gen;
     VM* vm;
     void (*_gc_on_delete)(VM*, PyObject*) = nullptr;
     void (*_gc_marker_ex)(VM*) = nullptr;

+ 3 - 3
include/pocketpy/lexer.h

@@ -30,7 +30,7 @@ constexpr const char* kTokens[] = {
 };
 
 using TokenValue = std::variant<std::monostate, i64, f64, Str>;
-const TokenIndex kTokenCount = sizeof(kTokens) / sizeof(kTokens[0]);
+const int kTokenCount = sizeof(kTokens) / sizeof(kTokens[0]);
 
 constexpr TokenIndex TK(const char token[]) {
     for(int k=0; k<kTokenCount; k++){
@@ -99,7 +99,7 @@ struct Lexer {
     const char* token_start;
     const char* curr_char;
     int current_line = 1;
-    std::vector<Token> nexts;
+    vector<Token> nexts;
     stack_no_copy<int, small_vector_2<int, 8>> indents;
     int brackets_level = 0;
 
@@ -129,7 +129,7 @@ struct Lexer {
     void SyntaxError(){ throw_err("SyntaxError", "invalid syntax"); }
     void IndentationError(Str msg){ throw_err("IndentationError", msg); }
     Lexer(VM* vm, std::shared_ptr<SourceData> src);
-    std::vector<Token> run();
+    vector<Token> run();
 };
 
 

+ 9 - 13
include/pocketpy/namedict.h

@@ -13,17 +13,11 @@ constexpr T default_invalid_value(){
     else return Discarded();
 }
 
-template<typename T>
-struct NameDictItem{
-    StrName first;
-    T second;
-};
-
 template<typename T>
 struct NameDictImpl {
     PK_ALWAYS_PASS_BY_POINTER(NameDictImpl)
 
-    using Item = NameDictItem<T>;
+    using Item = std::pair<StrName, T>;
     static constexpr uint16_t kInitialCapacity = 16;
     static_assert(is_pod_v<T>);
 
@@ -166,19 +160,21 @@ while(!_items[i].first.empty()) {           \
         return val;
     }
 
-    pod_vector<StrName> keys() const {
-        pod_vector<StrName> v;
+    array<StrName> keys() const {
+        array<StrName> v(_size);
+        int j = 0;
         for(uint16_t i=0; i<_capacity; i++){
             if(_items[i].first.empty()) continue;
-            v.push_back(_items[i].first);
+            new (&v[j++]) StrName(_items[i].first);
         }
         return v;
     }
 
-    pod_vector<NameDictItem<T>> items() const{
-        pod_vector<NameDictItem<T>> v;
+    array<Item> items() const{
+        array<Item> v(_size);
+        int j = 0;
         apply([&](StrName key, T val){
-            v.push_back(NameDictItem<T>{key, val});
+            new(&v[j++]) Item(key, val);
         });
         return v;
     }

+ 1 - 1
include/pocketpy/profiler.h

@@ -22,7 +22,7 @@ struct _FrameRecord{
 
 struct LineProfiler{
     // filename -> records
-    std::map<std::string_view, std::vector<_LineRecord>> records;
+    std::map<std::string_view, vector<_LineRecord>> records;
     stack_no_copy<_FrameRecord> frames;
     std::set<FuncDecl*> functions;
 

+ 6 - 6
include/pocketpy/str.h

@@ -77,8 +77,8 @@ struct Str{
     int index(const Str& sub, int start=0) const;
     Str replace(char old, char new_) const;
     Str replace(const Str& old, const Str& new_, int count=-1) const;
-    pod_vector<std::string_view> split(const Str& sep) const;
-    pod_vector<std::string_view> split(char sep) const;
+    vector<std::string_view> split(const Str& sep) const;
+    vector<std::string_view> split(char sep) const;
     int count(const Str& sub) const;
 
     /*************unicode*************/
@@ -127,15 +127,15 @@ struct StrName {
 
 struct SStream{
     PK_ALWAYS_PASS_BY_POINTER(SStream)
-    // pod_vector<T> is allocated by pool128 so the buffer can be moved into Str without a copy
-    pod_vector<char> buffer;
+
+    vector<char> buffer;
     int _precision = -1;
 
     bool empty() const { return buffer.empty(); }
     void setprecision(int precision) { _precision = precision; }
 
-    SStream(){}
-    SStream(int guess_size){ buffer.reserve(guess_size); }
+    SStream() {}
+    SStream(int guess_size) { buffer.reserve(guess_size);}
 
     Str str();
 

+ 2 - 2
include/pocketpy/tuplelist.h

@@ -35,8 +35,8 @@ struct Tuple {
     void _gc_mark(VM*) const;
 };
 
-struct List: pod_vector<PyVar>{
-    using pod_vector<PyVar>::pod_vector;
+struct List: public vector<PyVar>{
+    using vector<PyVar>::vector;
     void _gc_mark(VM*) const;
 
     Tuple to_tuple() const{

+ 161 - 92
include/pocketpy/vector.h

@@ -5,147 +5,216 @@
 
 namespace pkpy{
 
-template<typename T>
-struct pod_vector{
-    static constexpr int SizeT = sizeof(T);
-    static constexpr int N = 128 / SizeT;
-    static constexpr int Growth = 2;
-
-    // static_assert(128 % SizeT == 0);
-    static_assert(is_pod_v<T>);
-    static_assert(N >= 4);
+struct explicit_copy_t {
+    explicit explicit_copy_t() = default;
+};
 
-    int _size;
-    int _capacity;
+template<typename T>
+struct array{
     T* _data;
+    int _size;
 
     using size_type = int;
 
-    pod_vector(): _size(0), _capacity(N) {
-        _data = (T*)pool128_alloc(_capacity * SizeT);
+    array(): _data(nullptr), _size(0) {}
+    array(int size): _data((T*)malloc(sizeof(T) * size)), _size(size) {}
+    array(array&& other) noexcept: _data(other._data), _size(other._size) {
+        other._data = nullptr;
+        other._size = 0;
+    }
+    array(const array& other) = delete;
+    array(explicit_copy_t, const array& other) {
+        _data = (T*)malloc(sizeof(T) * other._size);
+        _size = other._size;
+        for(int i=0; i<_size; i++) _data[i] = other._data[i];
     }
 
-    // support initializer list
-    pod_vector(std::initializer_list<T> il): _size(il.size()), _capacity(std::max(N, _size)) {
-        _data = (T*)pool128_alloc(_capacity * SizeT);
-        for(int i=0; i<_size; i++) _data[i] = *(il.begin() + i);
+    array& operator=(array&& other) noexcept{
+        if(_data){
+            std::destroy(begin(), end());
+            free(_data);
+        }
+        _data = other._data;
+        _size = other._size;
+        other._data = nullptr;
+        other._size = 0;
+        return *this;
     }
 
-    pod_vector(int size): _size(size), _capacity(std::max(N, size)) {
-        _data = (T*)pool128_alloc(_capacity * SizeT);
+    array& operator=(const array& other) = delete;
+
+    T& operator[](int i) {
+        PK_DEBUG_ASSERT(i>=0 && i<_size);
+        return _data[i];
     }
 
-    pod_vector(const pod_vector& other): _size(other._size), _capacity(other._capacity) {
-        _data = (T*)pool128_alloc(_capacity * SizeT);
-        memcpy(_data, other._data, SizeT * _size);
+    const T& operator[](int i) const {
+        PK_DEBUG_ASSERT(i>=0 && i<_size);
+        return _data[i];
     }
 
-    pod_vector(pod_vector&& other) noexcept {
-        _size = other._size;
-        _capacity = other._capacity;
-        _data = other._data;
-        other._data = nullptr;
+    int size() const { return _size; }
+
+    T* begin() const{ return _data; }
+    T* end() const{ return _data + _size; }
+
+    std::pair<T*, int> detach() noexcept {
+        std::pair<T*, int> retval(_data, _size);
+        _data = nullptr;
+        _size = 0;
+        return retval;
     }
 
-    pod_vector& operator=(pod_vector&& other) noexcept {
-        if(_data!=nullptr) pool128_dealloc(_data);
-        _size = other._size;
-        _capacity = other._capacity;
-        _data = other._data;
-        other._data = nullptr;
-        return *this;
+    ~array() {
+        if(_data){
+            std::destroy(begin(), end());
+            free(_data);
+        }
     }
+};
 
-    // remove copy assignment
-    pod_vector& operator=(const pod_vector& other) = delete;
 
-    template<typename __ValueT>
-    void push_back(__ValueT&& t) {
-        if (_size == _capacity) reserve(_capacity*Growth);
-        _data[_size++] = std::forward<__ValueT>(t);
+template<typename T>
+struct vector{
+    T* _data;
+    int _capacity;
+    int _size;
+
+    using size_type = int;
+
+    vector(): _data(nullptr), _capacity(0), _size(0) {}
+    vector(int size):
+        _data((T*)malloc(sizeof(T) * size)),
+        _capacity(size), _size(size) {}
+    vector(vector&& other) noexcept:
+        _data(other._data), _capacity(other._capacity), _size(other._size) {
+        other._data = nullptr;
+        other._capacity = 0;
+        other._size = 0;
+    }
+    vector(const vector& other) = delete;
+    vector(explicit_copy_t, const vector& other):
+        _data((T*)malloc(sizeof(T) * other._size)),
+        _capacity(other._size), _size(other._size) {
+        for(int i=0; i<_size; i++) _data[i] = other._data[i];
     }
 
-    template<typename... Args>
-    void emplace_back(Args&&... args) {
-        if (_size == _capacity) reserve(_capacity*Growth);
-        new (&_data[_size++]) T(std::forward<Args>(args)...);
+    // allow move
+    vector& operator=(vector&& other) noexcept{
+        if(_data){
+            std::destroy(begin(), end());
+            free(_data);
+        }
+        new (this) vector(std::move(other));
+        return *this;
     }
+    // disallow copy
+    vector& operator=(const vector& other) = delete;
+
+    bool empty() const { return _size == 0; }
+    int size() const { return _size; }
+    int capacity() const { return _capacity; }
+    T& back() { return _data[_size-1]; }
+
+    T* begin() const { return _data; }
+    T* end() const { return _data + _size; }
+    T* data() const { return _data; }
 
     void reserve(int cap){
-        if(cap <= _capacity) return;
-        _capacity = cap;
-        T* old_data = _data;
-        _data = (T*)pool128_alloc(_capacity * SizeT);
-        if(old_data != nullptr){
-            memcpy(_data, old_data, SizeT * _size);
-            pool128_dealloc(old_data);
+        if(cap < 4) cap = 4;    // minimum capacity
+        if(cap <= capacity()) return;
+        T* new_data = (T*)malloc(sizeof(T) * cap);
+        if constexpr(std::is_trivially_copyable_v<T>){
+            memcpy(new_data, _data, sizeof(T) * _size);
+        }else{
+            for(int i=0; i<_size; i++){
+                new(&new_data[i]) T(std::move(_data[i]));
+                _data[i].~T();
+            }
         }
+        if(_data) free(_data);
+        _data = new_data;
+        _capacity = cap;
+    }
+
+    void resize(int size){
+        reserve(size);
+        _size = size;
     }
 
-    void pop_back() { _size--; }
-    T popx_back() { T t = std::move(_data[_size-1]); _size--; return t; }
-    
-    void extend(const pod_vector& other){
-        for(int i=0; i<other.size(); i++) push_back(other[i]);
+    void push_back(const T& t){
+        if(_size == _capacity) reserve(_capacity * 2);
+        new (&_data[_size++]) T(t);
     }
 
-    void extend(const T* begin, const T* end){
-        for(auto it=begin; it!=end; it++) push_back(*it);
+    void push_back(T&& t){
+        if(_size == _capacity) reserve(_capacity * 2);
+        new(&_data[_size++]) T(std::move(t));
     }
 
-    T& operator[](int index) { return _data[index]; }
-    const T& operator[](int index) const { return _data[index]; }
+    template<typename... Args>
+    void emplace_back(Args&&... args){
+        if(_size == _capacity) reserve(_capacity * 2);
+        new(&_data[_size++]) T(std::forward<Args>(args)...);
+    }
 
-    T* begin() { return _data; }
-    T* end() { return _data + _size; }
-    const T* begin() const { return _data; }
-    const T* end() const { return _data + _size; }
-    T& back() { return _data[_size - 1]; }
-    const T& back() const { return _data[_size - 1]; }
+    T& operator[](int i) { return _data[i]; }
+    const T& operator[](int i) const { return _data[i]; }
 
-    bool empty() const { return _size == 0; }
-    int size() const { return _size; }
-    T* data() { return _data; }
-    const T* data() const { return _data; }
-    void clear() { _size=0; }
-
-    template<typename __ValueT>
-    void insert(int i, __ValueT&& val){
-        if (_size == _capacity) reserve(_capacity*Growth);
-        for(int j=_size; j>i; j--) _data[j] = _data[j-1];
-        _data[i] = std::forward<__ValueT>(val);
+    void extend(T* begin, T* end){
+        int n = end - begin;
+        reserve(_size + n);
+        for(int i=0; i<n; i++) new(&_data[_size++]) T(begin[i]);
+    }
+
+    void insert(int index, const T& t){
+        if(_size == _capacity) reserve(_capacity * 2);
+        for(int i=_size; i>index; i--) _data[i] = std::move(_data[i-1]);
+        _data[index] = t;
         _size++;
     }
 
-    void erase(int i){
-        for(int j=i; j<_size-1; j++) _data[j] = _data[j+1];
+    void erase(int index){
+        for(int i=index; i<_size-1; i++) _data[i] = std::move(_data[i+1]);
         _size--;
     }
 
-    void reverse(){
-        std::reverse(_data, _data+_size);
+    void pop_back(){
+        PK_DEBUG_ASSERT(_size > 0);
+        _size--;
+        if constexpr(!std::is_trivially_destructible_v<T>){
+            _data[_size].~T();
+        }
     }
 
-    void resize(int size){
-        if(size > _capacity) reserve(size);
-        _size = size;
+    void clear(){
+        std::destroy(begin(), end());
+        _size = 0;
     }
 
     std::pair<T*, int> detach() noexcept {
-        T* p = _data;
-        int size = _size;
+        std::pair<T*, int> retval(_data, _size);
         _data = nullptr;
+        _capacity = 0;
         _size = 0;
-        return {p, size};
+        return retval;
     }
 
-    ~pod_vector() {
-        if(_data != nullptr) pool128_dealloc(_data);
+    void swap(vector& other){
+        std::swap(_data, other._data);
+        std::swap(_capacity, other._capacity);
+        std::swap(_size, other._size);
     }
-};
 
+    ~vector(){
+        if(_data){
+            std::destroy(begin(), end());
+            free(_data);
+        }
+    }
+};
 
-template <typename T, typename Container=std::vector<T>>
+template <typename T, typename Container=vector<T>>
 class stack{
 	Container vec;
 public:
@@ -167,7 +236,7 @@ public:
     const Container& container() const { return vec; }
 };
 
-template <typename T, typename Container=std::vector<T>>
+template <typename T, typename Container=vector<T>>
 class stack_no_copy: public stack<T, Container>{
 public:
     stack_no_copy() = default;

+ 4 - 4
include/pocketpy/vm.h

@@ -74,7 +74,7 @@ struct PyTypeInfo{
     PyTypeInfo(PyObject* obj, Type base, PyObject* mod, StrName name, bool subclass_enabled, Vt vt={}):
         obj(obj), base(base), mod(mod), name(name), subclass_enabled(subclass_enabled), vt(vt) {}
     
-    std::vector<StrName> annotated_fields = {};
+    vector<StrName> annotated_fields = {};
 
     // unary operators
     Str (*m__repr__)(VM* vm, PyVar) = nullptr;
@@ -127,8 +127,8 @@ struct PyTypeInfo{
 struct ImportContext{
     PK_ALWAYS_PASS_BY_POINTER(ImportContext)
 
-    std::vector<Str> pending;
-    std::vector<bool> pending_is_init;   // a.k.a __init__.py
+    vector<Str> pending;
+    vector<bool> pending_is_init;   // a.k.a __init__.py
 
     ImportContext() {}
 
@@ -159,7 +159,7 @@ public:
     ManagedHeap heap;
     ValueStack s_data;
     CallStack callstack;
-    std::vector<PyTypeInfo> _all_types;
+    vector<PyTypeInfo> _all_types;
     
     NameDict _modules;                                 // loaded modules
     std::map<StrName, Str> _lazy_modules;              // lazy loaded modules

+ 4 - 2
src/ceval.cpp

@@ -931,7 +931,7 @@ __NEXT_STEP:
             if(_1 == StopIteration) break;
             extras.push_back(_1);
         }
-        PUSH(VAR(extras));
+        PUSH(VAR(std::move(extras)));
     } DISPATCH()
     /*****************************************/
     case OP_BEGIN_CLASS:{
@@ -1056,7 +1056,9 @@ __NEXT_STEP:
                 Exception& _e = PK_OBJ_GET(Exception, e_obj);
                 bool is_base_frame_to_be_popped = frame == base_frame;
                 __pop_frame();
-                if(callstack.empty()) throw _e;   // propagate to the top level
+                if(callstack.empty()){
+                    throw std::move(_e);    // propagate to the top level
+                }
                 frame = &callstack.top();
                 PUSH(e_obj);
                 if(is_base_frame_to_be_popped){

+ 2 - 2
src/compiler.cpp

@@ -1292,7 +1292,7 @@ __EAT_DOTS_END:
         }
 
         int count = deserializer.read_count();
-        std::vector<Str>& precompiled_tokens = lexer.src->_precompiled_tokens;
+        vector<Str>& precompiled_tokens = lexer.src->_precompiled_tokens;
         for(int i=0; i<count; i++){
             precompiled_tokens.push_back(deserializer.read_string('\n'));
         }
@@ -1379,7 +1379,7 @@ __EAT_DOTS_END:
         PyVar e_obj = vm->call(vm->builtins->attr(type), VAR(msg));
         Exception& e = PK_OBJ_GET(Exception, e_obj);
         e.st_push(src, lineno, cursor, "");
-        throw e;
+        throw std::move(e);
     }
 
     std::string_view TokenDeserializer::read_string(char c){

+ 1 - 1
src/csv.cpp

@@ -73,7 +73,7 @@ __NEXT_LINE:
         if(ret.size() == 0){
             vm->ValueError("empty csvfile");
         }
-        List header = CAST(List&, ret[0]);
+        const List& header = CAST(List&, ret[0]);
         List new_ret;
         for(int i=1; i<ret.size(); i++){
             const List& row = CAST(List&, ret[i]);

+ 7 - 5
src/error.cpp

@@ -66,14 +66,16 @@ namespace pkpy{
     }
 
     Str Exception::summary() const {
-        stack<ExceptionLine> st(stacktrace);
         SStream ss;
         if(is_re) ss << "Traceback (most recent call last):\n";
-        while(!st.empty()) {
-            ss << st.top().snapshot() << '\n';
-            st.pop();
+        // while(!st.empty()) {
+        //     ss << st.top().snapshot() << '\n';
+        //     st.pop();
+        // }
+        const auto& container = stacktrace.container();
+        for(int i=container.size()-1; i>=0; i--){
+            ss << container[i].snapshot() << '\n';
         }
-        // TODO: allow users to override the behavior
         if (!msg.empty()) ss << type.sv() << ": " << msg;
         else ss << type.sv();
         return ss.str();

+ 2 - 1
src/gc.cpp

@@ -3,7 +3,8 @@
 namespace pkpy{
 
     int ManagedHeap::sweep(){
-        std::vector<PyObject*> alive;
+        vector<PyObject*> alive;
+        alive.reserve(gen.size() / 2);
         for(PyObject* obj: gen){
             if(obj->gc_marked){
                 obj->gc_marked = false;

+ 1 - 1
src/lexer.cpp

@@ -485,7 +485,7 @@ static bool is_unicode_Lo_char(uint32_t c) {
         this->indents.push(0);
     }
 
-    std::vector<Token> Lexer::run() {
+    vector<Token> Lexer::run() {
         PK_ASSERT(curr_char == src->source.c_str());
         while (lex_one_token());
         return std::move(nexts);

+ 19 - 13
src/pocketpy.cpp

@@ -622,7 +622,7 @@ void __init_builtins(VM* _vm) {
         const Str& self = _CAST(Str&, args[0]);
         const Str& sep = CAST(Str&, args[1]);
         if(sep.empty()) vm->ValueError("empty separator");
-        pod_vector<std::string_view> parts;
+        vector<std::string_view> parts;
         if(sep.size == 1){
             parts = self.split(sep[0]);
         }else{
@@ -635,8 +635,7 @@ void __init_builtins(VM* _vm) {
 
     _vm->bind(_vm->_t(VM::tp_str), "splitlines(self)", [](VM* vm, ArgsView args) {
         const Str& self = _CAST(Str&, args[0]);
-        pod_vector<std::string_view> parts;
-        parts = self.split('\n');
+        vector<std::string_view> parts = self.split('\n');
         List ret(parts.size());
         for(int i=0; i<parts.size(); i++) ret[i] = VAR(Str(parts[i]));
         return VAR(std::move(ret));
@@ -797,7 +796,9 @@ void __init_builtins(VM* _vm) {
             });
         }
         bool reverse = CAST(bool, args[2]);
-        if(reverse) self.reverse();
+        if(reverse){
+            std::reverse(self.begin(), self.end());
+        }
         return vm->None;
     });
 
@@ -892,7 +893,9 @@ void __init_builtins(VM* _vm) {
         List& self = _CAST(List&, args[0]);
         if(args.size() == 1+0){
             if(self.empty()) vm->IndexError("pop from empty list");
-            return self.popx_back();
+            PyVar retval = self.back();
+            self.pop_back();
+            return retval;
         }
         if(args.size() == 1+1){
             i64 index = CAST(i64, args[1]);
@@ -936,7 +939,7 @@ void __init_builtins(VM* _vm) {
         int n = _CAST(int, _1);
         List result;
         result.reserve(self.size() * n);
-        for(int i = 0; i < n; i++) result.extend(self);
+        for(int i = 0; i < n; i++) result.extend(self.begin(), self.end());
         return VAR(std::move(result));
     });
     _vm->bind_func(VM::tp_list, "__rmul__", 2, [](VM* vm, ArgsView args) {
@@ -945,7 +948,7 @@ void __init_builtins(VM* _vm) {
         int n = _CAST(int, args[1]);
         List result;
         result.reserve(self.size() * n);
-        for(int i = 0; i < n; i++) result.extend(self);
+        for(int i = 0; i < n; i++) result.extend(self.begin(), self.end());
         return VAR(std::move(result));
     });
 
@@ -964,11 +967,14 @@ void __init_builtins(VM* _vm) {
         return vm->None;
     });
 
-    _vm->bind_func(VM::tp_list, "copy", 1, PK_LAMBDA(VAR(_CAST(List, args[0]))));
+    _vm->bind_func(VM::tp_list, "copy", 1, [](VM* vm, ArgsView args){
+        const List& self = _CAST(List&, args[0]);
+        return VAR(List(explicit_copy_t(), self));
+    });
 
 #define BIND_RICH_CMP(name, op, _t, _T)    \
-    _vm->bind__##name##__(_vm->_t, [](VM* vm, PyVar lhs, PyVar rhs){        \
-        if(!is_type(rhs, vm->_t)) return vm->NotImplemented;             \
+    _vm->bind__##name##__(_vm->_t, [](VM* vm, PyVar lhs, PyVar rhs){                \
+        if(!is_type(rhs, vm->_t)) return vm->NotImplemented;                        \
         auto& a = _CAST(_T&, lhs);                                                  \
         auto& b = _CAST(_T&, rhs);                                                  \
         for(int i=0; i<a.size() && i<b.size(); i++){                                \
@@ -993,8 +999,8 @@ void __init_builtins(VM* _vm) {
     _vm->bind__add__(VM::tp_list, [](VM* vm, PyVar _0, PyVar _1) {
         const List& self = _CAST(List&, _0);
         const List& other = CAST(List&, _1);
-        List new_list(self);    // copy construct
-        new_list.extend(other);
+        List new_list(explicit_copy_t(), self);
+        new_list.extend(other.begin(), other.end());
         return VAR(std::move(new_list));
     });
 
@@ -1637,7 +1643,7 @@ void VM::__post_init_builtin_types(){
     try{
         // initialize dummy func_decl for exec/eval
         CodeObject_ dynamic_co = compile("def _(): pass", "<dynamic>", EXEC_MODE);
-        __dynamic_func_decl = dynamic_co->func_decls.at(0);
+        __dynamic_func_decl = dynamic_co->func_decls[0];
         // initialize builtins
         CodeObject_ code = compile(kPythonLibs_builtins, "<builtins>", EXEC_MODE);
         this->_exec(code, this->builtins);

+ 4 - 4
src/profiler.cpp

@@ -40,7 +40,7 @@ void LineProfiler::_step(int callstack_size, Frame* frame){
         }
     }
 
-    frames.top().prev_record = &file_records.at(line);
+    frames.top().prev_record = &file_records[line];
 }
 
 void LineProfiler::_step_end(int callstack_size, Frame* frame, int line){
@@ -87,11 +87,11 @@ Str LineProfiler::stats(){
         int end_line = decl->code->end_line;
         if(start_line == -1 || end_line == -1) continue;
         std::string_view filename = decl->code->src->filename.sv();
-        std::vector<_LineRecord>& file_records = records[filename];
+        vector<_LineRecord>& file_records = records[filename];
         if(file_records.empty()) continue;
         clock_t total_time = 0;
         for(int line = start_line; line <= end_line; line++){
-            total_time += file_records.at(line).time;
+            total_time += file_records[line].time;
         }
         ss << "Total time: " << (f64)total_time / CLOCKS_PER_SEC << "s\n";
         ss << "File: " << filename << "\n";
@@ -99,7 +99,7 @@ Str LineProfiler::stats(){
         ss << "Line #      Hits         Time  Per Hit   % Time  Line Contents\n";
         ss << "==============================================================\n";
         for(int line = start_line; line <= end_line; line++){
-            const _LineRecord& record = file_records.at(line);
+            const _LineRecord& record = file_records[line];
             if(!record.is_valid()) continue;
             ss << left_pad(std::to_string(line), 6);
             if(record.hits == 0){

+ 1 - 1
src/random.cpp

@@ -190,7 +190,7 @@ struct Random{
             PyVar* data = view.begin();
             int size = view.size();
             if(size == 0) vm->IndexError("cannot choose from an empty sequence");
-            pod_vector<f64> cum_weights(size);
+            array<f64> cum_weights(size);
             if(args[2] == vm->None){
                 for(int i = 0; i < size; i++) cum_weights[i] = i + 1;
             }else{

+ 15 - 9
src/str.cpp

@@ -333,8 +333,8 @@ int utf8len(unsigned char c, bool suppress){
         return _byte_index_to_unicode(size);
     }
 
-    pod_vector<std::string_view> Str::split(const Str& sep) const{
-        pod_vector<std::string_view> result;
+    vector<std::string_view> Str::split(const Str& sep) const{
+        vector<std::string_view> result;
         std::string_view tmp;
         int start = 0;
         while(true){
@@ -349,8 +349,8 @@ int utf8len(unsigned char c, bool suppress){
         return result;
     }
 
-    pod_vector<std::string_view> Str::split(char sep) const{
-        pod_vector<std::string_view> result;
+    vector<std::string_view> Str::split(char sep) const{
+        vector<std::string_view> result;
         int i = 0;
         for(int j = 0; j < size; j++){
             if(data[j] == sep){
@@ -404,29 +404,35 @@ int utf8len(unsigned char c, bool suppress){
     }
 
     Str SStream::str(){
+#if 0
         // after this call, the buffer is no longer valid
         buffer.reserve(buffer.size() + 1);  // allocate one more byte for '\0'
         buffer[buffer.size()] = '\0';       // set '\0'
         return Str(buffer.detach());
+#else
+#warning "SStream::str() needs to be optimized"
+        buffer.push_back('\0');
+        return Str(buffer.data(), buffer.size()-1);
+#endif
     }
 
     SStream& SStream::operator<<(const Str& s){
-        buffer.extend(s.begin(), s.end());
+        for(char c: s) buffer.push_back(c);
         return *this;
     }
 
     SStream& SStream::operator<<(const char* s){
-        buffer.extend(s, s + strlen(s));
+        while(*s) buffer.push_back(*s++);
         return *this;
     }
 
     SStream& SStream::operator<<(const std::string& s){
-        buffer.extend(s.data(), s.data() + s.size());
+        for(char c: s) buffer.push_back(c);
         return *this;
     }
 
     SStream& SStream::operator<<(std::string_view s){
-        buffer.extend(s.data(), s.data() + s.size());
+        for(char c: s) buffer.push_back(c);
         return *this;
     }
 
@@ -459,7 +465,7 @@ int utf8len(unsigned char c, bool suppress){
             buffer.push_back('-');
             val = -val;
         }
-        char* begin = buffer.end();
+        auto begin = buffer.end();
         while(val){
             buffer.push_back('0' + val % 10);
             val /= 10;

+ 6 - 6
src/vm.cpp

@@ -330,7 +330,7 @@ namespace pkpy{
 
     PyVar VM::py_import(Str path, bool throw_err){
         if(path.empty()) vm->ValueError("empty module name");
-        static auto f_join = [](const pod_vector<std::string_view>& cpnts){
+        static auto f_join = [](const vector<std::string_view>& cpnts){
             SStream ss;
             for(int i=0; i<cpnts.size(); i++){
                 if(i != 0) ss << ".";
@@ -346,7 +346,7 @@ namespace pkpy{
             Str curr_path = __import_context.pending.back();
             bool curr_is_init = __import_context.pending_is_init.back();
             // convert relative path to absolute path
-            pod_vector<std::string_view> cpnts = curr_path.split('.');
+            vector<std::string_view> cpnts = curr_path.split('.');
             int prefix = 0;     // how many dots in the prefix
             for(int i=0; i<path.length(); i++){
                 if(path[i] == '.') prefix++;
@@ -366,7 +366,7 @@ namespace pkpy{
         PyVar ext_mod = _modules.try_get(name);
         if(ext_mod != nullptr) return ext_mod;
 
-        pod_vector<std::string_view> path_cpnts = path.split('.');
+        vector<std::string_view> path_cpnts = path.split('.');
         // check circular import
         if(__import_context.pending.size() > 128){
             ImportError("maximum recursion depth exceeded while importing");
@@ -769,7 +769,7 @@ Str VM::disassemble(CodeObject_ co){
         return s + std::string(n - s.length(), ' ');
     };
 
-    std::vector<int> jumpTargets;
+    vector<int> jumpTargets;
     for(int i=0; i<co->codes.size(); i++){
         Bytecode byte = co->codes[i];
         if(byte.is_forward_jump()){
@@ -1442,7 +1442,7 @@ void VM::_error(PyVar e_obj){
     Exception& e = PK_OBJ_GET(Exception, e_obj);
     if(callstack.empty()){
         e.is_re = false;
-        throw e;
+        throw std::move(e);
     }
     PUSH(e_obj);
     __raise_exc();
@@ -1665,7 +1665,7 @@ void VM::__breakpoint(){
     bool show_headers = true;
     
     while(true){
-        std::vector<LinkedFrame*> frames;
+        vector<LinkedFrame*> frames;
         LinkedFrame* lf = callstack._tail;
         while(lf != nullptr){
             frames.push_back(lf);