zexus 1.7.2 → 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -6
- package/package.json +2 -1
- package/rust_core/Cargo.lock +603 -0
- package/rust_core/Cargo.toml +26 -0
- package/rust_core/README.md +15 -0
- package/rust_core/pyproject.toml +25 -0
- package/rust_core/src/binary_bytecode.rs +543 -0
- package/rust_core/src/contract_vm.rs +643 -0
- package/rust_core/src/executor.rs +847 -0
- package/rust_core/src/hasher.rs +90 -0
- package/rust_core/src/lib.rs +71 -0
- package/rust_core/src/merkle.rs +128 -0
- package/rust_core/src/rust_vm.rs +2313 -0
- package/rust_core/src/signature.rs +79 -0
- package/rust_core/src/state_adapter.rs +281 -0
- package/rust_core/src/validator.rs +116 -0
- package/scripts/postinstall.js +34 -2
- package/src/zexus/__init__.py +1 -1
- package/src/zexus/blockchain/accelerator.py +27 -0
- package/src/zexus/blockchain/contract_vm.py +409 -3
- package/src/zexus/blockchain/rust_bridge.py +64 -0
- package/src/zexus/cli/main.py +1 -1
- package/src/zexus/cli/zpm.py +1 -1
- package/src/zexus/evaluator/bytecode_compiler.py +150 -52
- package/src/zexus/evaluator/core.py +151 -809
- package/src/zexus/evaluator/expressions.py +27 -22
- package/src/zexus/evaluator/functions.py +171 -126
- package/src/zexus/evaluator/statements.py +55 -112
- package/src/zexus/module_cache.py +20 -9
- package/src/zexus/object.py +330 -38
- package/src/zexus/parser/parser.py +69 -14
- package/src/zexus/parser/strategy_context.py +228 -5
- package/src/zexus/parser/strategy_structural.py +2 -2
- package/src/zexus/persistence.py +46 -17
- package/src/zexus/security.py +140 -234
- package/src/zexus/type_checker.py +44 -5
- package/src/zexus/vm/binary_bytecode.py +7 -3
- package/src/zexus/vm/bytecode.py +6 -0
- package/src/zexus/vm/cache.py +24 -46
- package/src/zexus/vm/compiler.py +80 -20
- package/src/zexus/vm/fastops.c +1093 -2975
- package/src/zexus/vm/gas_metering.py +2 -2
- package/src/zexus/vm/memory_pool.py +21 -9
- package/src/zexus/vm/vm.py +527 -67
- package/src/zexus/zpm/package_manager.py +1 -1
- package/src/zexus.egg-info/PKG-INFO +79 -12
- package/src/zexus.egg-info/SOURCES.txt +23 -1
- package/src/zexus.egg-info/requires.txt +26 -0
- package/src/zexus.egg-info/entry_points.txt +0 -4
package/src/zexus/vm/cache.py
CHANGED
|
@@ -17,7 +17,6 @@ Enhanced: File-based persistent caching for faster repeat runs
|
|
|
17
17
|
|
|
18
18
|
import hashlib
|
|
19
19
|
import json
|
|
20
|
-
import pickle
|
|
21
20
|
import time
|
|
22
21
|
from collections import OrderedDict
|
|
23
22
|
from dataclasses import dataclass
|
|
@@ -271,8 +270,9 @@ class BytecodeCache:
|
|
|
271
270
|
constants_size = 0
|
|
272
271
|
for const in bytecode.constants:
|
|
273
272
|
try:
|
|
274
|
-
|
|
275
|
-
|
|
273
|
+
import sys as _sys
|
|
274
|
+
constants_size += _sys.getsizeof(const)
|
|
275
|
+
except (TypeError, ValueError):
|
|
276
276
|
constants_size += 100 # Fallback estimate
|
|
277
277
|
|
|
278
278
|
# Add metadata overhead
|
|
@@ -504,9 +504,9 @@ class BytecodeCache:
|
|
|
504
504
|
with open(cache_file, 'wb') as f:
|
|
505
505
|
f.write(data)
|
|
506
506
|
else:
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
507
|
+
# SECURITY (H14): Legacy pickle format disabled — use .zxc only
|
|
508
|
+
if self.debug:
|
|
509
|
+
print(f"⚠️ Cache: Skipping disk save (pickle disabled, .zxc not available)")
|
|
510
510
|
|
|
511
511
|
if self.debug:
|
|
512
512
|
print(f"💾 Cache: Saved to disk {key[:8]}...")
|
|
@@ -531,14 +531,9 @@ class BytecodeCache:
|
|
|
531
531
|
print(f"💾 Cache: Loaded .zxc from disk {key[:8]}...")
|
|
532
532
|
return bytecode
|
|
533
533
|
|
|
534
|
-
#
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
with open(cache_file, 'rb') as f:
|
|
538
|
-
bytecode = pickle.load(f)
|
|
539
|
-
if self.debug:
|
|
540
|
-
print(f"💾 Cache: Loaded .cache from disk {key[:8]}...")
|
|
541
|
-
return bytecode
|
|
534
|
+
# SECURITY (H14): Legacy pickle .cache format disabled
|
|
535
|
+
# Only .zxc binary format is supported for disk cache
|
|
536
|
+
pass
|
|
542
537
|
except Exception as e:
|
|
543
538
|
if self.debug:
|
|
544
539
|
print(f"⚠️ Cache: Failed to load from disk: {e}")
|
|
@@ -713,10 +708,9 @@ class BytecodeCache:
|
|
|
713
708
|
f.write(meta_bytes)
|
|
714
709
|
f.write(bc_bytes)
|
|
715
710
|
else:
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
pickle.dump(data, f, protocol=pickle.HIGHEST_PROTOCOL)
|
|
711
|
+
# SECURITY (H14): Legacy pickle format disabled
|
|
712
|
+
if self.debug:
|
|
713
|
+
print(f"⚠️ FileCache: Skipping disk save (pickle disabled, .zxc not available)")
|
|
720
714
|
|
|
721
715
|
if self.debug:
|
|
722
716
|
print(f"💾 FileCache: Saved to disk {file_key[:8]}...")
|
|
@@ -749,16 +743,8 @@ class BytecodeCache:
|
|
|
749
743
|
bytecodes = _zxc_deserialize_multi(raw[4 + meta_len:])
|
|
750
744
|
cache_file = zxc_file
|
|
751
745
|
|
|
752
|
-
#
|
|
753
|
-
|
|
754
|
-
pkl_file = self.cache_dir / f"file_{file_key}.cache"
|
|
755
|
-
if pkl_file.exists():
|
|
756
|
-
with open(pkl_file, 'rb') as f:
|
|
757
|
-
data = pickle.load(f)
|
|
758
|
-
cached_meta = data.get('metadata', {})
|
|
759
|
-
bytecodes = data.get('bytecodes', [])
|
|
760
|
-
cached_at = data.get('cached_at', time.time())
|
|
761
|
-
cache_file = pkl_file
|
|
746
|
+
# SECURITY (H14): Legacy pickle format disabled
|
|
747
|
+
pass
|
|
762
748
|
|
|
763
749
|
if cached_meta is None or bytecodes is None:
|
|
764
750
|
return None
|
|
@@ -810,10 +796,11 @@ class BytecodeCache:
|
|
|
810
796
|
return
|
|
811
797
|
|
|
812
798
|
try:
|
|
813
|
-
|
|
799
|
+
# SECURITY (H14): Use JSON instead of pickle for index
|
|
800
|
+
index_file = self.cache_dir / 'file_index.json'
|
|
814
801
|
if index_file.exists():
|
|
815
|
-
with open(index_file, '
|
|
816
|
-
index =
|
|
802
|
+
with open(index_file, 'r') as f:
|
|
803
|
+
index = json.load(f)
|
|
817
804
|
if self.debug:
|
|
818
805
|
print(f"📂 FileCache: Loaded index with {len(index)} files")
|
|
819
806
|
except Exception as e:
|
|
@@ -826,7 +813,8 @@ class BytecodeCache:
|
|
|
826
813
|
return
|
|
827
814
|
|
|
828
815
|
try:
|
|
829
|
-
|
|
816
|
+
# SECURITY (H14): Use JSON instead of pickle for index
|
|
817
|
+
index_file = self.cache_dir / 'file_index.json'
|
|
830
818
|
# Just save file keys and metadata (not bytecode)
|
|
831
819
|
index = {
|
|
832
820
|
key: {
|
|
@@ -837,8 +825,8 @@ class BytecodeCache:
|
|
|
837
825
|
}
|
|
838
826
|
for key, data in self._file_cache.items()
|
|
839
827
|
}
|
|
840
|
-
with open(index_file, '
|
|
841
|
-
|
|
828
|
+
with open(index_file, 'w') as f:
|
|
829
|
+
json.dump(index, f)
|
|
842
830
|
except Exception as e:
|
|
843
831
|
if self.debug:
|
|
844
832
|
print(f"⚠️ FileCache: Failed to save index: {e}")
|
|
@@ -985,18 +973,8 @@ class BytecodeCache:
|
|
|
985
973
|
cached.get('content_hash') == metadata.content_hash):
|
|
986
974
|
return True
|
|
987
975
|
|
|
988
|
-
#
|
|
989
|
-
|
|
990
|
-
cache_file = self.cache_dir / f"file_{file_key}.cache"
|
|
991
|
-
if cache_file.exists():
|
|
992
|
-
try:
|
|
993
|
-
with open(cache_file, 'rb') as f:
|
|
994
|
-
data = pickle.load(f)
|
|
995
|
-
cached_meta = data.get('metadata', {})
|
|
996
|
-
return (cached_meta.get('mtime') == metadata.mtime and
|
|
997
|
-
cached_meta.get('content_hash') == metadata.content_hash)
|
|
998
|
-
except Exception:
|
|
999
|
-
pass
|
|
976
|
+
# SECURITY (H14): Legacy pickle .cache disk cache disabled
|
|
977
|
+
# Only .zxc format is used for disk caching now
|
|
1000
978
|
|
|
1001
979
|
return False
|
|
1002
980
|
|
package/src/zexus/vm/compiler.py
CHANGED
|
@@ -139,6 +139,20 @@ class BytecodeCompiler:
|
|
|
139
139
|
f"Failed to compile {node_type}: {exc}"
|
|
140
140
|
) from exc
|
|
141
141
|
|
|
142
|
+
def _emit_vm_builtin_call(self, builtin_name: str, payload, discard_result: bool = True) -> None:
|
|
143
|
+
"""Emit a VM-builtin call with the AST payload as a constant.
|
|
144
|
+
|
|
145
|
+
This is used to keep the VM bytecode compiler aligned with the evaluator
|
|
146
|
+
bytecode compiler for directive-like statements that the VM can delegate
|
|
147
|
+
to higher-level helpers when available.
|
|
148
|
+
"""
|
|
149
|
+
payload_idx = self._add_constant(payload)
|
|
150
|
+
self._emit(Opcode.LOAD_CONST, payload_idx)
|
|
151
|
+
name_idx = self._add_constant(builtin_name)
|
|
152
|
+
self._emit(Opcode.CALL_NAME, (name_idx, 1))
|
|
153
|
+
if discard_result:
|
|
154
|
+
self._emit(Opcode.POP)
|
|
155
|
+
|
|
142
156
|
def _unsupported_message(self, node_type: str) -> str:
|
|
143
157
|
"""Return a friendly message for unsupported nodes."""
|
|
144
158
|
hints = {
|
|
@@ -801,6 +815,38 @@ class BytecodeCompiler:
|
|
|
801
815
|
self._emit(Opcode.CALL_NAME, (gc_idx, 1))
|
|
802
816
|
self._emit(Opcode.POP)
|
|
803
817
|
|
|
818
|
+
# ------------------------------------------------------------------
|
|
819
|
+
# Directive-like statements (delegate to VM helpers when available)
|
|
820
|
+
# ------------------------------------------------------------------
|
|
821
|
+
|
|
822
|
+
def _compile_NativeStatement(self, node):
|
|
823
|
+
"""Compile native statement via VM helper (if provided)."""
|
|
824
|
+
self._emit_vm_builtin_call("__vm_native_statement__", node, discard_result=False)
|
|
825
|
+
|
|
826
|
+
def _compile_InlineStatement(self, node):
|
|
827
|
+
"""Compile inline optimization directive via VM helper."""
|
|
828
|
+
self._emit_vm_builtin_call("__vm_inline_statement__", node, discard_result=False)
|
|
829
|
+
|
|
830
|
+
def _compile_BufferStatement(self, node):
|
|
831
|
+
"""Compile buffer directive via VM helper."""
|
|
832
|
+
self._emit_vm_builtin_call("__vm_buffer_statement__", node, discard_result=False)
|
|
833
|
+
|
|
834
|
+
def _compile_SIMDStatement(self, node):
|
|
835
|
+
"""Compile SIMD directive via VM helper."""
|
|
836
|
+
self._emit_vm_builtin_call("__vm_simd_statement__", node, discard_result=False)
|
|
837
|
+
|
|
838
|
+
def _compile_DeferStatement(self, node):
|
|
839
|
+
"""Compile defer directive via VM helper."""
|
|
840
|
+
self._emit_vm_builtin_call("__vm_defer_statement__", node, discard_result=False)
|
|
841
|
+
|
|
842
|
+
def _compile_EmitStatement(self, node):
|
|
843
|
+
"""Compile emit statement via VM helper."""
|
|
844
|
+
self._emit_vm_builtin_call("__vm_emit_statement__", node, discard_result=False)
|
|
845
|
+
|
|
846
|
+
def _compile_ProtocolStatement(self, node):
|
|
847
|
+
"""Compile protocol statement via VM helper."""
|
|
848
|
+
self._emit_vm_builtin_call("__vm_protocol_statement__", node, discard_result=False)
|
|
849
|
+
|
|
804
850
|
|
|
805
851
|
def _compile_ContractStatement(self, node):
|
|
806
852
|
"""Compile smart contract definition into bytecode that creates a real
|
|
@@ -939,31 +985,45 @@ class BytecodeCompiler:
|
|
|
939
985
|
# Push name
|
|
940
986
|
self._emit(Opcode.LOAD_CONST, name_idx)
|
|
941
987
|
|
|
942
|
-
# Compile
|
|
988
|
+
# Compile properties and methods from EntityStatement's actual attributes
|
|
943
989
|
member_count = 0
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
990
|
+
|
|
991
|
+
# Compile properties (list of dicts with 'name', 'type', 'default_value')
|
|
992
|
+
for prop in (node.properties or []):
|
|
993
|
+
prop_name = prop.get('name', '') if isinstance(prop, dict) else getattr(prop, 'name', '')
|
|
994
|
+
default_val = prop.get('default_value', None) if isinstance(prop, dict) else getattr(prop, 'default_value', None)
|
|
995
|
+
|
|
996
|
+
if default_val is not None:
|
|
997
|
+
self._compile_node(default_val)
|
|
998
|
+
else:
|
|
999
|
+
self._emit(Opcode.LOAD_CONST, self._add_constant(None))
|
|
1000
|
+
self._emit(Opcode.LOAD_CONST, self._add_constant(prop_name))
|
|
1001
|
+
member_count += 1
|
|
1002
|
+
|
|
1003
|
+
# Compile methods (list of ActionStatement)
|
|
1004
|
+
for method in (node.methods or []):
|
|
1005
|
+
self._compile_node(method)
|
|
1006
|
+
method_name = method.name.value if hasattr(method.name, 'value') else str(method.name)
|
|
1007
|
+
self._emit(Opcode.LOAD_NAME, self._add_constant(method_name))
|
|
1008
|
+
self._emit(Opcode.LOAD_CONST, self._add_constant(method_name))
|
|
1009
|
+
member_count += 1
|
|
963
1010
|
|
|
964
1011
|
self._emit(Opcode.DEFINE_ENTITY, member_count)
|
|
965
1012
|
self._emit(Opcode.STORE_NAME, name_idx)
|
|
966
1013
|
|
|
1014
|
+
def _compile_ExportStatement(self, node):
|
|
1015
|
+
"""Compile export statement - marks names for module export.
|
|
1016
|
+
|
|
1017
|
+
Export is a declaration/annotation; the exported names are already
|
|
1018
|
+
defined by their own declarations, so this just emits EXPORT opcodes
|
|
1019
|
+
to register them in the module's export table.
|
|
1020
|
+
"""
|
|
1021
|
+
for name_node in (node.names or []):
|
|
1022
|
+
name = name_node.value if hasattr(name_node, 'value') else str(name_node)
|
|
1023
|
+
name_idx = self._add_constant(name)
|
|
1024
|
+
self._emit(Opcode.LOAD_NAME, name_idx)
|
|
1025
|
+
self._emit(Opcode.EXPORT, name_idx)
|
|
1026
|
+
|
|
967
1027
|
def _compile_DataStatement(self, node):
|
|
968
1028
|
"""Compile Data/Dataclass definition"""
|
|
969
1029
|
# Data objects are simpler entities
|