cpyte 3.3.2__tar.gz → 3.3.3__tar.gz
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.
- {cpyte-3.3.2 → cpyte-3.3.3}/MANIFEST.in +1 -0
- {cpyte-3.3.2/source/cpyte.egg-info → cpyte-3.3.3}/PKG-INFO +1 -1
- {cpyte-3.3.2 → cpyte-3.3.3}/pyproject.toml +1 -1
- cpyte-3.3.3/source/cpyte/__init__.py +1 -0
- {cpyte-3.3.2 → cpyte-3.3.3}/source/cpyte/astparse.py +2 -2
- {cpyte-3.3.2 → cpyte-3.3.3}/source/cpyte/bytecoding.py +4 -1
- {cpyte-3.3.2 → cpyte-3.3.3}/source/cpyte/mainpie.py +25 -7
- {cpyte-3.3.2 → cpyte-3.3.3}/source/cpyte/semantic_analasis.py +43 -13
- cpyte-3.3.3/source/cpyte/ugc.h +426 -0
- {cpyte-3.3.2 → cpyte-3.3.3/source/cpyte.egg-info}/PKG-INFO +1 -1
- {cpyte-3.3.2 → cpyte-3.3.3}/source/cpyte.egg-info/SOURCES.txt +1 -0
- cpyte-3.3.2/source/cpyte/__init__.py +0 -1
- {cpyte-3.3.2 → cpyte-3.3.3}/readme.md +0 -0
- {cpyte-3.3.2 → cpyte-3.3.3}/setup.cfg +0 -0
- {cpyte-3.3.2 → cpyte-3.3.3}/source/cpyte/__main__.py +0 -0
- {cpyte-3.3.2 → cpyte-3.3.3}/source/cpyte/_bignum_bc.py +0 -0
- {cpyte-3.3.2 → cpyte-3.3.3}/source/cpyte/_runtime_bc.py +0 -0
- {cpyte-3.3.2 → cpyte-3.3.3}/source/cpyte/bignum.c +0 -0
- {cpyte-3.3.2 → cpyte-3.3.3}/source/cpyte/clib.py +0 -0
- {cpyte-3.3.2 → cpyte-3.3.3}/source/cpyte/compiling.py +0 -0
- {cpyte-3.3.2 → cpyte-3.3.3}/source/cpyte/extension_hooks.py +0 -0
- {cpyte-3.3.2 → cpyte-3.3.3}/source/cpyte/formatter.py +0 -0
- {cpyte-3.3.2 → cpyte-3.3.3}/source/cpyte/gc_runtime.c +0 -0
- {cpyte-3.3.2 → cpyte-3.3.3}/source/cpyte/generate_bc.py +0 -0
- {cpyte-3.3.2 → cpyte-3.3.3}/source/cpyte/lexar.py +0 -0
- {cpyte-3.3.2 → cpyte-3.3.3}/source/cpyte/linker.py +0 -0
- {cpyte-3.3.2 → cpyte-3.3.3}/source/cpyte/lsp_server.py +0 -0
- {cpyte-3.3.2 → cpyte-3.3.3}/source/cpyte/package_manifest.py +0 -0
- {cpyte-3.3.2 → cpyte-3.3.3}/source/cpyte/runtime.c +0 -0
- {cpyte-3.3.2 → cpyte-3.3.3}/source/cpyte/runtime_scorpion.c +0 -0
- {cpyte-3.3.2 → cpyte-3.3.3}/source/cpyte/sef.py +0 -0
- {cpyte-3.3.2 → cpyte-3.3.3}/source/cpyte/ui.py +0 -0
- {cpyte-3.3.2 → cpyte-3.3.3}/source/cpyte/update_check.py +0 -0
- {cpyte-3.3.2 → cpyte-3.3.3}/source/cpyte.egg-info/dependency_links.txt +0 -0
- {cpyte-3.3.2 → cpyte-3.3.3}/source/cpyte.egg-info/entry_points.txt +0 -0
- {cpyte-3.3.2 → cpyte-3.3.3}/source/cpyte.egg-info/requires.txt +0 -0
- {cpyte-3.3.2 → cpyte-3.3.3}/source/cpyte.egg-info/top_level.txt +0 -0
- {cpyte-3.3.2 → cpyte-3.3.3}/test/test_bignum_jit.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "3.3.3"
|
|
@@ -1245,10 +1245,10 @@ def parse_import(tokens: list[Token], pos: int):
|
|
|
1245
1245
|
assert first is not None
|
|
1246
1246
|
parts = [first]
|
|
1247
1247
|
pos += 1
|
|
1248
|
-
while pos < len(tokens) and tokens[pos].type
|
|
1248
|
+
while pos < len(tokens) and tokens[pos].type in (TokenType.DOT, TokenType.SLASH):
|
|
1249
1249
|
pos += 1
|
|
1250
1250
|
if pos >= len(tokens) or tokens[pos].type != TokenType.IDENTIFIER:
|
|
1251
|
-
raise ParseError('Expected identifier after
|
|
1251
|
+
raise ParseError('Expected identifier after package separator in package name', tokens[pos] if pos < len(tokens) else None)
|
|
1252
1252
|
part = tokens[pos].value
|
|
1253
1253
|
assert part is not None
|
|
1254
1254
|
parts.append(part)
|
|
@@ -2050,7 +2050,10 @@ class LLVM:
|
|
|
2050
2050
|
if parent_struct and parent_struct in self.struct_fields:
|
|
2051
2051
|
return self._field_type_name(parent_struct, node.name)
|
|
2052
2052
|
if isinstance(node, Index):
|
|
2053
|
-
|
|
2053
|
+
base = self._struct_name_from_node(node.obj)
|
|
2054
|
+
if base:
|
|
2055
|
+
return self._base_type_name(base)
|
|
2056
|
+
return None
|
|
2054
2057
|
return None
|
|
2055
2058
|
|
|
2056
2059
|
def _emit_lvalue_attr(self, node: Attr):
|
|
@@ -7,14 +7,14 @@ if __package__:
|
|
|
7
7
|
from .astparse import Import, ParseError, parse_file
|
|
8
8
|
from .bytecoding import LLVM
|
|
9
9
|
from .compiling import (
|
|
10
|
-
_RUNTIME_C,
|
|
11
10
|
_GC_RUNTIME_C,
|
|
11
|
+
_RUNTIME_C,
|
|
12
12
|
optimize,
|
|
13
13
|
run_aot,
|
|
14
14
|
run_jit,
|
|
15
15
|
run_scorpion,
|
|
16
16
|
)
|
|
17
|
-
from .extension_hooks import
|
|
17
|
+
from .extension_hooks import HookStage, get_global_hook_registry
|
|
18
18
|
from .lexar import Lexer, LexerError, register_keywords
|
|
19
19
|
from .linker import Linker
|
|
20
20
|
from .package_manifest import (
|
|
@@ -32,14 +32,14 @@ else:
|
|
|
32
32
|
from cpyte.astparse import Import, ParseError, parse_file
|
|
33
33
|
from cpyte.bytecoding import LLVM
|
|
34
34
|
from cpyte.compiling import (
|
|
35
|
-
_RUNTIME_C,
|
|
36
35
|
_GC_RUNTIME_C,
|
|
36
|
+
_RUNTIME_C,
|
|
37
37
|
optimize,
|
|
38
38
|
run_aot,
|
|
39
39
|
run_jit,
|
|
40
40
|
run_scorpion,
|
|
41
41
|
)
|
|
42
|
-
from cpyte.extension_hooks import
|
|
42
|
+
from cpyte.extension_hooks import HookStage, get_global_hook_registry
|
|
43
43
|
from cpyte.lexar import Lexer, LexerError, register_keywords
|
|
44
44
|
from cpyte.linker import Linker
|
|
45
45
|
from cpyte.package_manifest import (
|
|
@@ -326,10 +326,26 @@ def _notify_deep_hook_usage() -> None:
|
|
|
326
326
|
)
|
|
327
327
|
|
|
328
328
|
|
|
329
|
-
def
|
|
329
|
+
def _find_workspace_root(filepath: str | None) -> str:
|
|
330
|
+
if not filepath:
|
|
331
|
+
return os.getcwd()
|
|
332
|
+
path = os.path.abspath(filepath)
|
|
333
|
+
dirpath = os.path.dirname(path)
|
|
334
|
+
while True:
|
|
335
|
+
if os.path.isdir(os.path.join(dirpath, ".cpm")):
|
|
336
|
+
return dirpath
|
|
337
|
+
parent = os.path.dirname(dirpath)
|
|
338
|
+
if parent == dirpath:
|
|
339
|
+
return os.getcwd()
|
|
340
|
+
dirpath = parent
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def _compile(source, tab_size=4, strict=False, enable_extensions=True, no_gc=False, filepath=None):
|
|
344
|
+
workspace_root = _find_workspace_root(filepath)
|
|
345
|
+
|
|
330
346
|
# Pre-load package manifests if extensions are enabled
|
|
331
347
|
if enable_extensions:
|
|
332
|
-
_load_package_manifests_from_source(
|
|
348
|
+
_load_package_manifests_from_source(workspace_root)
|
|
333
349
|
|
|
334
350
|
lex = Lexer(source, tab_size=tab_size, enable_extensions=enable_extensions)
|
|
335
351
|
tokens = lex.get_tokens()
|
|
@@ -342,7 +358,8 @@ def _compile(source, tab_size=4, strict=False, enable_extensions=True, no_gc=Fal
|
|
|
342
358
|
source,
|
|
343
359
|
parsed,
|
|
344
360
|
strict=strict,
|
|
345
|
-
workspace_root=
|
|
361
|
+
workspace_root=workspace_root,
|
|
362
|
+
filepath=filepath,
|
|
346
363
|
enable_extensions=enable_extensions,
|
|
347
364
|
no_gc=no_gc,
|
|
348
365
|
)
|
|
@@ -839,6 +856,7 @@ def _main():
|
|
|
839
856
|
strict=strict,
|
|
840
857
|
enable_extensions=not no_userspace,
|
|
841
858
|
no_gc=no_gc,
|
|
859
|
+
filepath=os.path.abspath(args[0]),
|
|
842
860
|
)
|
|
843
861
|
|
|
844
862
|
if mode == "ast":
|
|
@@ -1125,6 +1125,18 @@ class SemanticAnalyzer:
|
|
|
1125
1125
|
node,
|
|
1126
1126
|
note="cannot divide integer by zero",
|
|
1127
1127
|
)
|
|
1128
|
+
if left_t is not None and right_t is not None:
|
|
1129
|
+
left_ptr = left_t.endswith("*")
|
|
1130
|
+
right_ptr = right_t.endswith("*")
|
|
1131
|
+
if node.op.name in ("PLUS", "MINUS") and (
|
|
1132
|
+
(left_ptr and right_t == "int")
|
|
1133
|
+
or (right_ptr and left_t == "int")
|
|
1134
|
+
):
|
|
1135
|
+
node.inferred_type = left_t if left_ptr else right_t
|
|
1136
|
+
return node.inferred_type
|
|
1137
|
+
if node.op.name == "MINUS" and left_ptr and right_ptr:
|
|
1138
|
+
node.inferred_type = "int"
|
|
1139
|
+
return "int"
|
|
1128
1140
|
if left_t is not None and right_t is not None and left_t != right_t:
|
|
1129
1141
|
# Usual arithmetic conversions: promote to the widest type
|
|
1130
1142
|
promoted = self._numeric_promote(left_t, right_t)
|
|
@@ -1996,21 +2008,33 @@ class SemanticAnalyzer:
|
|
|
1996
2008
|
return None
|
|
1997
2009
|
|
|
1998
2010
|
def _try_cpm_import(self, node: Import, module: str) -> bool:
|
|
1999
|
-
#
|
|
2000
|
-
|
|
2001
|
-
pkg_name =
|
|
2002
|
-
if pkg_name in self._loaded_packages:
|
|
2003
|
-
# Package was loaded via manifest, register empty symbols and succeed
|
|
2004
|
-
self._register_import_symbols({}, node)
|
|
2005
|
-
return True
|
|
2011
|
+
# Support both @scope.name and @scope/name import forms.
|
|
2012
|
+
import_name = module[1:] if module.startswith("@") else module
|
|
2013
|
+
pkg_name = import_name.replace("/", ".").split(".")[-1]
|
|
2006
2014
|
|
|
2007
2015
|
cpm_root = None
|
|
2008
2016
|
if self._workspace_root:
|
|
2009
2017
|
cpm_root = os.path.join(self._workspace_root, ".cpm", "modules")
|
|
2010
2018
|
elif self._filedir:
|
|
2011
2019
|
cpm_root = os.path.join(self._filedir, "..", ".cpm", "modules")
|
|
2020
|
+
|
|
2021
|
+
# A package already loaded purely via its manifest (extension hooks only)
|
|
2022
|
+
# has no real symbols; short-circuit only when no CPM directory exists to
|
|
2023
|
+
# resolve a real entry from. A package that ships an entry file must still
|
|
2024
|
+
# re-export its actual symbols below.
|
|
2025
|
+
if pkg_name in self._loaded_packages and not (
|
|
2026
|
+
cpm_root and os.path.isdir(cpm_root)
|
|
2027
|
+
):
|
|
2028
|
+
self._register_import_symbols({}, node)
|
|
2029
|
+
return True
|
|
2030
|
+
|
|
2012
2031
|
if cpm_root and os.path.isdir(cpm_root):
|
|
2013
|
-
|
|
2032
|
+
if module.startswith("@"):
|
|
2033
|
+
scoped = module[1:].replace("/", ".").replace(".", os.sep)
|
|
2034
|
+
pkg_dir = os.path.join(cpm_root, f"@{scoped.split(os.sep)[0]}", *scoped.split(os.sep)[1:])
|
|
2035
|
+
else:
|
|
2036
|
+
pkg_dir = os.path.join(cpm_root, *module.replace("/", ".").split("."))
|
|
2037
|
+
|
|
2014
2038
|
if os.path.isdir(pkg_dir):
|
|
2015
2039
|
versions = sorted(
|
|
2016
2040
|
[
|
|
@@ -2021,10 +2045,8 @@ class SemanticAnalyzer:
|
|
|
2021
2045
|
reverse=True,
|
|
2022
2046
|
)
|
|
2023
2047
|
if versions:
|
|
2024
|
-
pkg_name = module.lstrip("@").rsplit("/", 1)[-1]
|
|
2025
2048
|
version_dir = os.path.join(pkg_dir, versions[0])
|
|
2026
2049
|
|
|
2027
|
-
# Load package manifest if extensions are enabled
|
|
2028
2050
|
manifest_loaded = False
|
|
2029
2051
|
if self.enable_extensions:
|
|
2030
2052
|
manifest_loaded = self._load_package_manifest(
|
|
@@ -2041,12 +2063,12 @@ class SemanticAnalyzer:
|
|
|
2041
2063
|
if result:
|
|
2042
2064
|
return True
|
|
2043
2065
|
|
|
2044
|
-
# If no .cpy or prebuilt files but manifest was loaded, allow the import
|
|
2045
|
-
# This supports extension-only packages
|
|
2046
2066
|
if manifest_loaded:
|
|
2047
|
-
# Register empty symbols for extension-only packages
|
|
2048
2067
|
self._register_import_symbols({}, node)
|
|
2049
2068
|
return True
|
|
2069
|
+
if self.enable_extensions and pkg_name in self._loaded_packages:
|
|
2070
|
+
self._register_import_symbols({}, node)
|
|
2071
|
+
return True
|
|
2050
2072
|
return False
|
|
2051
2073
|
|
|
2052
2074
|
def _import_cpy(self, module: str, node: Import | None = None):
|
|
@@ -2097,6 +2119,12 @@ class SemanticAnalyzer:
|
|
|
2097
2119
|
if fname not in symbols:
|
|
2098
2120
|
symbols[fname] = (ret_type, params, vararg)
|
|
2099
2121
|
|
|
2122
|
+
# Preserve the sub-AST chain for re-exported imports so codegen
|
|
2123
|
+
# can still reach the aggregated module's function definitions
|
|
2124
|
+
# (emit_program recurses through Import sub_ast).
|
|
2125
|
+
if getattr(ast_node, "sub_ast", None):
|
|
2126
|
+
sub_ast.append(ast_node)
|
|
2127
|
+
|
|
2100
2128
|
if node is not None:
|
|
2101
2129
|
node.sub_ast = sub_ast
|
|
2102
2130
|
return symbols, "cpy"
|
|
@@ -3335,11 +3363,13 @@ def analyze(
|
|
|
3335
3363
|
nodes: list,
|
|
3336
3364
|
strict: bool = False,
|
|
3337
3365
|
workspace_root: str | None = None,
|
|
3366
|
+
filepath: str | None = None,
|
|
3338
3367
|
enable_extensions: bool = True,
|
|
3339
3368
|
no_gc: bool = False,
|
|
3340
3369
|
):
|
|
3341
3370
|
analyzer = SemanticAnalyzer(
|
|
3342
3371
|
source,
|
|
3372
|
+
filepath=filepath,
|
|
3343
3373
|
strict=strict,
|
|
3344
3374
|
workspace_root=workspace_root,
|
|
3345
3375
|
enable_extensions=enable_extensions,
|
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
#ifndef UGC_H
|
|
2
|
+
#define UGC_H
|
|
3
|
+
|
|
4
|
+
#include <stddef.h>
|
|
5
|
+
#include <stdint.h>
|
|
6
|
+
|
|
7
|
+
#ifndef UGC_DECL
|
|
8
|
+
#define UGC_DECL
|
|
9
|
+
#endif
|
|
10
|
+
|
|
11
|
+
#ifndef UGC_USE_TAGGED_POINTER
|
|
12
|
+
#define UGC_USE_TAGGED_POINTER 1
|
|
13
|
+
#endif
|
|
14
|
+
|
|
15
|
+
typedef struct ugc_s ugc_t;
|
|
16
|
+
typedef struct ugc_header_s ugc_header_t;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @brief Callback function type.
|
|
20
|
+
* @see ugc_init
|
|
21
|
+
*/
|
|
22
|
+
typedef void(*ugc_visit_fn_t)(ugc_t* gc, ugc_header_t* obj);
|
|
23
|
+
|
|
24
|
+
enum ugc_state_e
|
|
25
|
+
{
|
|
26
|
+
UGC_IDLE,
|
|
27
|
+
UGC_MARK,
|
|
28
|
+
UGC_SWEEP
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
enum ugc_barrier_direction_e
|
|
32
|
+
{
|
|
33
|
+
UGC_BARRIER_FORWARD,
|
|
34
|
+
UGC_BARRIER_BACKWARD,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/// Header for a managed object. All fields MUST NOT be accessed.
|
|
38
|
+
struct ugc_header_s
|
|
39
|
+
{
|
|
40
|
+
ugc_header_t* next;
|
|
41
|
+
ugc_header_t* prev;
|
|
42
|
+
#if !UGC_USE_TAGGED_POINTER
|
|
43
|
+
unsigned color: 2;
|
|
44
|
+
#endif
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @brief Garbage collector data
|
|
49
|
+
*
|
|
50
|
+
* All fields MUST NOT be accessed unless stated otherwise.
|
|
51
|
+
*/
|
|
52
|
+
struct ugc_s
|
|
53
|
+
{
|
|
54
|
+
ugc_header_t set1, set2;
|
|
55
|
+
ugc_header_t *from, *to, *iterator;
|
|
56
|
+
ugc_visit_fn_t scan_fn, release_fn;
|
|
57
|
+
|
|
58
|
+
/// Arbitrary userdata, not used by the library.
|
|
59
|
+
void* userdata;
|
|
60
|
+
|
|
61
|
+
/// Current state of the garbage collection. Read-only.
|
|
62
|
+
unsigned char state;
|
|
63
|
+
unsigned char white;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @brief Initialize a GC.
|
|
68
|
+
*
|
|
69
|
+
* Two callbacks are required: one for scanning and the other for releasing.
|
|
70
|
+
*
|
|
71
|
+
* The scan callback must do the following:
|
|
72
|
+
*
|
|
73
|
+
* - If `obj` is NULL, it must call ugc_visit on all root objects.
|
|
74
|
+
* - If `obj` is not NULL, it must call ugc_visit on all objects referenced by
|
|
75
|
+
* the given object.
|
|
76
|
+
*
|
|
77
|
+
* The release callback will be invoked whenever the GC has determined that
|
|
78
|
+
* an object has become garbage.
|
|
79
|
+
*/
|
|
80
|
+
UGC_DECL void
|
|
81
|
+
ugc_init(ugc_t* gc, ugc_visit_fn_t scan_fn, ugc_visit_fn_t release_fn);
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* @brief Release all allocated objects.
|
|
85
|
+
*
|
|
86
|
+
* This is faster than clearing the root and forcing a full collection.
|
|
87
|
+
*/
|
|
88
|
+
UGC_DECL void
|
|
89
|
+
ugc_release_all(ugc_t* gc);
|
|
90
|
+
|
|
91
|
+
/// Register a new object to be managed by the GC.
|
|
92
|
+
UGC_DECL void
|
|
93
|
+
ugc_register(ugc_t* gc, ugc_header_t* obj);
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* @brief Execute a write barrier.
|
|
97
|
+
*
|
|
98
|
+
* Whenever an object stores a reference to another object, this function MUST
|
|
99
|
+
* be called to ensure that the GC works correctly.
|
|
100
|
+
*
|
|
101
|
+
* Root objects (stack, globals) are treated differently so there is no need to
|
|
102
|
+
* call this function when a store to them occurs.
|
|
103
|
+
*
|
|
104
|
+
* @remarks Both objects MUST NOT be NULL.
|
|
105
|
+
*/
|
|
106
|
+
UGC_DECL void
|
|
107
|
+
ugc_write_barrier(
|
|
108
|
+
ugc_t* gc,
|
|
109
|
+
enum ugc_barrier_direction_e direction,
|
|
110
|
+
ugc_header_t* parent,
|
|
111
|
+
ugc_header_t* child
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* @brief Make the GC perform one unit of work.
|
|
116
|
+
*
|
|
117
|
+
* What happens depends on the current GC's state.
|
|
118
|
+
*
|
|
119
|
+
* - In UGC_IDLE state, it will scan the root by calling the scan callback then
|
|
120
|
+
* switch to UGC_MARK state.
|
|
121
|
+
* - In UGC_MARK state, it will mark one object and discover its children using
|
|
122
|
+
* the scan callback. When there is no object left to mark, the GC will scan
|
|
123
|
+
* the root once more to account for changes during the mark phase. When all
|
|
124
|
+
* live objects are marked, it will switch to UGC_SWEEP state.
|
|
125
|
+
* - In UGC_SWEEP state, it will release one object. When all garbage are
|
|
126
|
+
* released, it wil switch to UGC_IDLE state.
|
|
127
|
+
*
|
|
128
|
+
* @see ugc_init
|
|
129
|
+
*/
|
|
130
|
+
UGC_DECL void
|
|
131
|
+
ugc_step(ugc_t* gc);
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* @brief Perform a collection cycle.
|
|
136
|
+
*
|
|
137
|
+
* Start the GC if it's not already running and only return once the GC has
|
|
138
|
+
* finished collecting all garbage identified at the point of calling.
|
|
139
|
+
*
|
|
140
|
+
* @remarks If the GC is already in the UGC_SWEEP state, it will leave newly
|
|
141
|
+
* created garbage for the next cycle.
|
|
142
|
+
*/
|
|
143
|
+
UGC_DECL void
|
|
144
|
+
ugc_collect(ugc_t* gc);
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* @brief Inform the GC of a referred object during the mark phase.
|
|
148
|
+
*
|
|
149
|
+
* @remarks This function MUST ONLY be called inside the scan callback.
|
|
150
|
+
* @remarks The provided object MUST NOT be NULL.
|
|
151
|
+
* @see ugc_init
|
|
152
|
+
*/
|
|
153
|
+
UGC_DECL void
|
|
154
|
+
ugc_visit(ugc_t* gc, ugc_header_t* obj);
|
|
155
|
+
|
|
156
|
+
#ifdef UGC_IMPLEMENTATION
|
|
157
|
+
|
|
158
|
+
#define UGC_GRAY 2
|
|
159
|
+
|
|
160
|
+
#if UGC_USE_TAGGED_POINTER
|
|
161
|
+
|
|
162
|
+
#define UGC_PTR(ptr) ((uintptr_t)ptr & (~0x03))
|
|
163
|
+
#define UGC_TAG(ptr) ((uintptr_t)ptr & 0x03)
|
|
164
|
+
#define UGC_SET_PTR(ptr, val) \
|
|
165
|
+
do { ptr = (ugc_header_t*)((uintptr_t)val | UGC_TAG(ptr)); } while(0)
|
|
166
|
+
#define UGC_SET_TAG(ptr, val) \
|
|
167
|
+
do { ptr = (ugc_header_t*)(UGC_PTR(ptr) | (uintptr_t)val); } while(0)
|
|
168
|
+
|
|
169
|
+
static inline void
|
|
170
|
+
ugc_set_next(ugc_header_t* obj, ugc_header_t* value)
|
|
171
|
+
{
|
|
172
|
+
UGC_SET_PTR(obj->next, value);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
static inline ugc_header_t*
|
|
176
|
+
ugc_next(ugc_header_t* obj)
|
|
177
|
+
{
|
|
178
|
+
return (ugc_header_t*)UGC_PTR(obj->next);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
static inline void
|
|
182
|
+
ugc_set_prev(ugc_header_t* obj, ugc_header_t* value)
|
|
183
|
+
{
|
|
184
|
+
UGC_SET_PTR(obj->prev, value);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
static inline ugc_header_t*
|
|
188
|
+
ugc_prev(ugc_header_t* obj)
|
|
189
|
+
{
|
|
190
|
+
return (ugc_header_t*)UGC_PTR(obj->prev);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
static inline void
|
|
194
|
+
ugc_set_color(ugc_header_t* obj, unsigned char color)
|
|
195
|
+
{
|
|
196
|
+
UGC_SET_TAG(obj->next, color);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
static inline unsigned char
|
|
200
|
+
ugc_color(ugc_header_t* obj)
|
|
201
|
+
{
|
|
202
|
+
return (unsigned char)UGC_TAG(obj->next);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
#else
|
|
206
|
+
|
|
207
|
+
static inline void
|
|
208
|
+
ugc_set_next(ugc_header_t* obj, ugc_header_t* value)
|
|
209
|
+
{
|
|
210
|
+
obj->next = value;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
static inline ugc_header_t*
|
|
214
|
+
ugc_next(ugc_header_t* obj)
|
|
215
|
+
{
|
|
216
|
+
return obj->next;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
static inline void
|
|
220
|
+
ugc_set_prev(ugc_header_t* obj, ugc_header_t* value)
|
|
221
|
+
{
|
|
222
|
+
obj->prev = value;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
static inline ugc_header_t*
|
|
226
|
+
ugc_prev(ugc_header_t* obj)
|
|
227
|
+
{
|
|
228
|
+
return obj->prev;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
static inline void
|
|
232
|
+
ugc_set_color(ugc_header_t* obj, unsigned char color)
|
|
233
|
+
{
|
|
234
|
+
obj->color = color;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
static unsigned char
|
|
238
|
+
ugc_color(ugc_header_t* obj)
|
|
239
|
+
{
|
|
240
|
+
return obj->color;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
#endif
|
|
244
|
+
|
|
245
|
+
static void
|
|
246
|
+
ugc_push(ugc_header_t* list, ugc_header_t* element)
|
|
247
|
+
{
|
|
248
|
+
ugc_set_next(element, list);
|
|
249
|
+
ugc_set_prev(element, list->prev);
|
|
250
|
+
ugc_set_next(list->prev, element);
|
|
251
|
+
ugc_set_prev(list, element);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
static void
|
|
255
|
+
ugc_unlink(ugc_header_t* element)
|
|
256
|
+
{
|
|
257
|
+
ugc_header_t* next = ugc_next(element);
|
|
258
|
+
ugc_header_t* prev = ugc_prev(element);
|
|
259
|
+
|
|
260
|
+
ugc_set_prev(next, prev);
|
|
261
|
+
ugc_set_next(prev, next);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
static void
|
|
265
|
+
ugc_make_gray(ugc_t* gc, ugc_header_t* obj)
|
|
266
|
+
{
|
|
267
|
+
// Move iterator back before moving obj
|
|
268
|
+
if(obj == gc->iterator) { gc->iterator = ugc_prev(obj); }
|
|
269
|
+
|
|
270
|
+
ugc_unlink(obj);
|
|
271
|
+
ugc_push(gc->to, obj);
|
|
272
|
+
ugc_set_color(obj, UGC_GRAY);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
static void
|
|
276
|
+
ugc_clear(ugc_header_t* list)
|
|
277
|
+
{
|
|
278
|
+
list->next = list;
|
|
279
|
+
list->prev = list;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
static void
|
|
283
|
+
ugc_release_set(ugc_t* gc, ugc_header_t* set)
|
|
284
|
+
{
|
|
285
|
+
for(ugc_header_t* itr = ugc_next(set); itr != set;)
|
|
286
|
+
{
|
|
287
|
+
ugc_header_t* next = ugc_next(itr);
|
|
288
|
+
|
|
289
|
+
gc->release_fn(gc, itr);
|
|
290
|
+
|
|
291
|
+
itr = next;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
void
|
|
296
|
+
ugc_init(ugc_t* gc, ugc_visit_fn_t scan_fn, ugc_visit_fn_t release_fn)
|
|
297
|
+
{
|
|
298
|
+
ugc_clear(&gc->set1);
|
|
299
|
+
ugc_clear(&gc->set2);
|
|
300
|
+
|
|
301
|
+
gc->state = UGC_IDLE;
|
|
302
|
+
gc->scan_fn = scan_fn;
|
|
303
|
+
gc->release_fn = release_fn;
|
|
304
|
+
gc->white = 0;
|
|
305
|
+
gc->from = &gc->set1;
|
|
306
|
+
gc->to = &gc->set2;
|
|
307
|
+
gc->iterator = gc->to;
|
|
308
|
+
gc->userdata = NULL;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
void
|
|
312
|
+
ugc_register(ugc_t* gc, ugc_header_t* obj)
|
|
313
|
+
{
|
|
314
|
+
ugc_push(gc->from, obj);
|
|
315
|
+
ugc_set_color(obj, gc->white);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
void
|
|
319
|
+
ugc_release_all(ugc_t* gc)
|
|
320
|
+
{
|
|
321
|
+
ugc_release_set(gc, gc->from);
|
|
322
|
+
ugc_release_set(gc, gc->to);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
void
|
|
326
|
+
ugc_write_barrier(
|
|
327
|
+
ugc_t* gc,
|
|
328
|
+
enum ugc_barrier_direction_e direction,
|
|
329
|
+
ugc_header_t* parent,
|
|
330
|
+
ugc_header_t* child
|
|
331
|
+
)
|
|
332
|
+
{
|
|
333
|
+
unsigned char white = gc->white;
|
|
334
|
+
unsigned char black = !gc->white;
|
|
335
|
+
|
|
336
|
+
if(ugc_color(parent) == black && ugc_color(child) == white)
|
|
337
|
+
{
|
|
338
|
+
switch(direction)
|
|
339
|
+
{
|
|
340
|
+
case UGC_BARRIER_FORWARD:
|
|
341
|
+
ugc_make_gray(gc, child);
|
|
342
|
+
break;
|
|
343
|
+
case UGC_BARRIER_BACKWARD:
|
|
344
|
+
ugc_make_gray(gc, parent);
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
void
|
|
351
|
+
ugc_visit(ugc_t* gc, ugc_header_t* obj)
|
|
352
|
+
{
|
|
353
|
+
if(ugc_color(obj) == gc->white)
|
|
354
|
+
{
|
|
355
|
+
ugc_make_gray(gc, obj);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
void
|
|
360
|
+
ugc_step(ugc_t* gc)
|
|
361
|
+
{
|
|
362
|
+
ugc_header_t* obj;
|
|
363
|
+
ugc_header_t* to = gc->to;
|
|
364
|
+
|
|
365
|
+
switch((enum ugc_state_e)gc->state)
|
|
366
|
+
{
|
|
367
|
+
case UGC_IDLE:
|
|
368
|
+
gc->scan_fn(gc, NULL);
|
|
369
|
+
gc->state = UGC_MARK;
|
|
370
|
+
break;
|
|
371
|
+
case UGC_MARK:
|
|
372
|
+
{
|
|
373
|
+
obj = ugc_next(gc->iterator);
|
|
374
|
+
unsigned char white = gc->white;
|
|
375
|
+
|
|
376
|
+
if(obj != to)
|
|
377
|
+
{
|
|
378
|
+
gc->iterator = obj;
|
|
379
|
+
ugc_set_color(obj, !white);
|
|
380
|
+
gc->scan_fn(gc, obj);
|
|
381
|
+
}
|
|
382
|
+
else
|
|
383
|
+
{
|
|
384
|
+
gc->scan_fn(gc, NULL);
|
|
385
|
+
obj = ugc_next(gc->iterator);
|
|
386
|
+
if(obj == to)
|
|
387
|
+
{
|
|
388
|
+
// Since we can get interrupted during the sweep phase,
|
|
389
|
+
// swap "from" and "to" set, flip white color before
|
|
390
|
+
// starting the sweep phase.
|
|
391
|
+
ugc_header_t* from = gc->from;
|
|
392
|
+
gc->from = to;
|
|
393
|
+
gc->to = from;
|
|
394
|
+
gc->white = !white;
|
|
395
|
+
gc->iterator = from->next;
|
|
396
|
+
gc->state = UGC_SWEEP;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
break;
|
|
401
|
+
case UGC_SWEEP:
|
|
402
|
+
obj = gc->iterator;
|
|
403
|
+
if(obj != to)
|
|
404
|
+
{
|
|
405
|
+
gc->iterator = ugc_next(obj);
|
|
406
|
+
gc->release_fn(gc, obj);
|
|
407
|
+
}
|
|
408
|
+
else
|
|
409
|
+
{
|
|
410
|
+
ugc_clear(to);
|
|
411
|
+
gc->state = UGC_IDLE;
|
|
412
|
+
}
|
|
413
|
+
break;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
void
|
|
418
|
+
ugc_collect(ugc_t* gc)
|
|
419
|
+
{
|
|
420
|
+
if(gc->state == UGC_IDLE) { ugc_step(gc); }
|
|
421
|
+
while(gc->state != UGC_IDLE) { ugc_step(gc); }
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
#endif
|
|
425
|
+
|
|
426
|
+
#endif
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "3.3.2"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|