hython-lang 2.0.0__py3-none-any.whl
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.
- hython/__init__.py +15 -0
- hython/__main__.py +3 -0
- hython/bytecode.py +368 -0
- hython/cli.py +449 -0
- hython/compiler.py +870 -0
- hython/compiler_manager.py +227 -0
- hython/diagnostics.py +158 -0
- hython/environment.py +48 -0
- hython/exe_builder.py +330 -0
- hython/frontend.py +870 -0
- hython/hir.py +93 -0
- hython/importer.py +76 -0
- hython/package_manager.py +343 -0
- hython/phonetics.py +49 -0
- hython/runtime.py +110 -0
- hython/runtime_manager.py +83 -0
- hython/translator.py +244 -0
- hython/updater.py +93 -0
- hython/vm.py +2409 -0
- hython/vocabulary.py +132 -0
- hython_lang-2.0.0.dist-info/METADATA +327 -0
- hython_lang-2.0.0.dist-info/RECORD +26 -0
- hython_lang-2.0.0.dist-info/WHEEL +5 -0
- hython_lang-2.0.0.dist-info/entry_points.txt +2 -0
- hython_lang-2.0.0.dist-info/licenses/LICENSE +22 -0
- hython_lang-2.0.0.dist-info/top_level.txt +1 -0
hython/__init__.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Hython: Python pronounced in Hangul."""
|
|
2
|
+
|
|
3
|
+
from .environment import activate_package_store
|
|
4
|
+
|
|
5
|
+
activate_package_store()
|
|
6
|
+
|
|
7
|
+
__version__ = "2.0.0"
|
|
8
|
+
|
|
9
|
+
from .translator import audit_english, koreanize, to_hython, to_python
|
|
10
|
+
from .importer import install_importer
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"audit_english", "koreanize", "to_hython", "to_python",
|
|
14
|
+
"install_importer", "__version__",
|
|
15
|
+
]
|
hython/__main__.py
ADDED
hython/bytecode.py
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
"""HBC container and instruction model; unrelated to CPython bytecode."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
import hashlib
|
|
4
|
+
import json
|
|
5
|
+
import struct
|
|
6
|
+
import zlib
|
|
7
|
+
import base64
|
|
8
|
+
from dataclasses import asdict, dataclass, field
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
MAGIC = b"HYBC"
|
|
12
|
+
VERSION = 6
|
|
13
|
+
MAX_COMPRESSED_SIZE = 64 * 1024 * 1024
|
|
14
|
+
MAX_INSTRUCTIONS = 1_000_000
|
|
15
|
+
_NO_ARG = {"RETURN","POP","NOP","ITER","SET_ITEM","GET_ITEM","FORMAT","NEG","POS","NOT","IMPORT_STAR",
|
|
16
|
+
"ADD","SUB","MUL","DIV","FLOORDIV","MOD","POW","BIT_OR","BIT_XOR","BIT_AND","LSHIFT","RSHIFT","MATMUL","IADD","ISUB","IMUL","IDIV","IFLOORDIV","IMOD","IPOW","IBIT_OR","IBIT_XOR","IBIT_AND","ILSHIFT","IRSHIFT","IMATMUL","EQ","NE","LT","LE","GT","GE","IN","IS","NOT_IN","IS_NOT","BOOL_AND","BOOL_OR","INVERT","RAISE","RAISE_FROM","RERAISE","DELETE_ITEM","ASSERT","DUP","DUP2","BUILD_SLICE","YIELD","YIELD_FROM","AWAIT","SIGNAL_RETURN","SIGNAL_BREAK","SIGNAL_CONTINUE","MAKE_INTERPOLATION","CALL_BEGIN","CALL_READY","CLASS_BEGIN","COLLECTION_READY"}
|
|
17
|
+
_NAME_ARG = {"LOAD","STORE","STORE_GLOBAL","STORE_NONLOCAL","DELETE","DELETE_GLOBAL","DELETE_NONLOCAL","GET_ATTR","SET_ATTR","DELETE_ATTR","IMPORT","ANNOTATE","SUPER","SAVE_NAME","RESTORE_NAME"}
|
|
18
|
+
_INT_ARG = {"CALL","UNPACK","UNPACK_EX","BUILD_LIST","BUILD_TUPLE","BUILD_SET","BUILD_DICT","BUILD_STRING","BUILD_TEMPLATE"}
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class CodeObject:
|
|
22
|
+
name: str
|
|
23
|
+
parameters: list[str]
|
|
24
|
+
constants: list[object]
|
|
25
|
+
instructions: list[list]
|
|
26
|
+
lines: list[int] = field(default_factory=list)
|
|
27
|
+
|
|
28
|
+
def to_dict(self) -> dict:
|
|
29
|
+
return {"name":self.name,"parameters":self.parameters,"constants":[_encode_constant(value) for value in self.constants],"instructions":self.instructions,"lines":self.lines}
|
|
30
|
+
|
|
31
|
+
@classmethod
|
|
32
|
+
def from_dict(cls, value: dict) -> "CodeObject":
|
|
33
|
+
return cls(value["name"], value["parameters"], [_decode_constant(item) for item in value["constants"]], value["instructions"],value.get("lines",[]))
|
|
34
|
+
|
|
35
|
+
def _encode_constant(value):
|
|
36
|
+
if value is Ellipsis: return {"$hython_constant":"ellipsis"}
|
|
37
|
+
if isinstance(value,bytes): return {"$hython_constant":"bytes","data":base64.b64encode(value).decode("ascii")}
|
|
38
|
+
if isinstance(value,complex): return {"$hython_constant":"complex","real":value.real,"imag":value.imag}
|
|
39
|
+
if isinstance(value,tuple): return {"$hython_constant":"tuple","items":[_encode_constant(item) for item in value]}
|
|
40
|
+
return value
|
|
41
|
+
|
|
42
|
+
def _decode_constant(value):
|
|
43
|
+
if not isinstance(value,dict) or "$hython_constant" not in value: return value
|
|
44
|
+
kind=value["$hython_constant"]
|
|
45
|
+
if kind=="ellipsis": return Ellipsis
|
|
46
|
+
if kind=="bytes": return base64.b64decode(value["data"],validate=True)
|
|
47
|
+
if kind=="complex": return complex(value["real"],value["imag"])
|
|
48
|
+
if kind=="tuple": return tuple(_decode_constant(item) for item in value["items"])
|
|
49
|
+
raise ValueError("알 수 없는 HBC 상수 형식")
|
|
50
|
+
|
|
51
|
+
class BytecodeError(ValueError):
|
|
52
|
+
pass
|
|
53
|
+
|
|
54
|
+
def dumps(code: CodeObject) -> bytes:
|
|
55
|
+
raw = json.dumps(code.to_dict(), ensure_ascii=False, separators=(",", ":")).encode("utf-8")
|
|
56
|
+
payload = zlib.compress(raw, level=9)
|
|
57
|
+
digest = hashlib.sha256(payload).digest()
|
|
58
|
+
return MAGIC + bytes([VERSION]) + struct.pack(">I", len(payload)) + digest + payload
|
|
59
|
+
|
|
60
|
+
def loads(data: bytes) -> CodeObject:
|
|
61
|
+
if len(data) < 41 or data[:4] != MAGIC:
|
|
62
|
+
raise BytecodeError("하이썬 HBC 파일이 아닙니다.")
|
|
63
|
+
if data[4] != VERSION:
|
|
64
|
+
raise BytecodeError(f"지원하지 않는 HBC 버전: {data[4]}")
|
|
65
|
+
size = struct.unpack(">I", data[5:9])[0]
|
|
66
|
+
if size > MAX_COMPRESSED_SIZE:
|
|
67
|
+
raise BytecodeError("HBC 파일이 허용 크기를 초과합니다.")
|
|
68
|
+
digest, payload = data[9:41], data[41:]
|
|
69
|
+
if size != len(payload) or hashlib.sha256(payload).digest() != digest:
|
|
70
|
+
raise BytecodeError("HBC 크기 또는 SHA-256 무결성 검사가 실패했습니다.")
|
|
71
|
+
try:
|
|
72
|
+
decompressor=zlib.decompressobj()
|
|
73
|
+
raw=decompressor.decompress(payload,MAX_COMPRESSED_SIZE+1)
|
|
74
|
+
if len(raw)>MAX_COMPRESSED_SIZE or decompressor.unconsumed_tail:
|
|
75
|
+
raise BytecodeError("HBC 압축 해제 크기가 제한을 초과합니다.")
|
|
76
|
+
value = json.loads(raw.decode("utf-8"))
|
|
77
|
+
code=CodeObject.from_dict(value)
|
|
78
|
+
verify(code)
|
|
79
|
+
return code
|
|
80
|
+
except BytecodeError:
|
|
81
|
+
raise
|
|
82
|
+
except (ValueError, KeyError, TypeError, zlib.error) as exc:
|
|
83
|
+
raise BytecodeError("손상된 HBC 내용입니다.") from exc
|
|
84
|
+
|
|
85
|
+
def write(path: Path, code: CodeObject) -> None:
|
|
86
|
+
path.write_bytes(dumps(code))
|
|
87
|
+
|
|
88
|
+
def read(path: Path) -> CodeObject:
|
|
89
|
+
return loads(path.read_bytes())
|
|
90
|
+
|
|
91
|
+
def _verify_target(spec) -> None:
|
|
92
|
+
if isinstance(spec,str): return
|
|
93
|
+
if not isinstance(spec,dict): raise TypeError
|
|
94
|
+
kind=spec.get("kind")
|
|
95
|
+
if kind=="name":
|
|
96
|
+
if not isinstance(spec.get("name"),str) or spec.get("scope","local") not in ("local","global","nonlocal"): raise TypeError
|
|
97
|
+
elif kind=="starred": _verify_target(spec["target"])
|
|
98
|
+
elif kind=="sequence":
|
|
99
|
+
if not isinstance(spec.get("items"),list): raise TypeError
|
|
100
|
+
for item in spec["items"]: _verify_target(item)
|
|
101
|
+
elif kind=="attribute":
|
|
102
|
+
if not isinstance(spec.get("name"),str): raise TypeError
|
|
103
|
+
verify(CodeObject.from_dict(spec["object"]))
|
|
104
|
+
elif kind=="subscript":
|
|
105
|
+
verify(CodeObject.from_dict(spec["object"])); verify(CodeObject.from_dict(spec["index"]))
|
|
106
|
+
else: raise TypeError
|
|
107
|
+
|
|
108
|
+
def _verify_pattern(spec) -> None:
|
|
109
|
+
if not isinstance(spec,dict): raise TypeError
|
|
110
|
+
kind=spec.get("kind"); scopes=("local","global","nonlocal")
|
|
111
|
+
if kind in ("wildcard","literal","singleton"): return
|
|
112
|
+
if kind in ("capture","star"):
|
|
113
|
+
if not isinstance(spec.get("name"),str) or spec.get("scope","local") not in scopes: raise TypeError
|
|
114
|
+
return
|
|
115
|
+
if kind=="value":
|
|
116
|
+
if not isinstance(spec.get("path"),str): raise TypeError
|
|
117
|
+
return
|
|
118
|
+
if kind=="or" or kind=="sequence":
|
|
119
|
+
key="items"
|
|
120
|
+
if not isinstance(spec.get(key),list): raise TypeError
|
|
121
|
+
for item in spec[key]: _verify_pattern(item)
|
|
122
|
+
return
|
|
123
|
+
if kind=="as":
|
|
124
|
+
if not isinstance(spec.get("name"),str) or spec.get("scope","local") not in scopes: raise TypeError
|
|
125
|
+
_verify_pattern(spec["pattern"]); return
|
|
126
|
+
if kind=="mapping":
|
|
127
|
+
if spec.get("rest") is not None and not isinstance(spec["rest"],str): raise TypeError
|
|
128
|
+
if spec.get("rest_scope","local") not in scopes or not isinstance(spec.get("pairs"),list): raise TypeError
|
|
129
|
+
for pair in spec["pairs"]:
|
|
130
|
+
key=pair["key"]
|
|
131
|
+
if not isinstance(key,dict) or key.get("kind") not in ("literal","value"): raise TypeError
|
|
132
|
+
if key["kind"]=="value" and not isinstance(key.get("path"),str): raise TypeError
|
|
133
|
+
_verify_pattern(pair["pattern"])
|
|
134
|
+
return
|
|
135
|
+
if kind=="class":
|
|
136
|
+
if not isinstance(spec.get("name"),str) or not isinstance(spec.get("positional"),list) or not isinstance(spec.get("keywords"),list): raise TypeError
|
|
137
|
+
for item in spec["positional"]: _verify_pattern(item)
|
|
138
|
+
for item in spec["keywords"]:
|
|
139
|
+
if not isinstance(item.get("name"),str): raise TypeError
|
|
140
|
+
_verify_pattern(item["pattern"])
|
|
141
|
+
return
|
|
142
|
+
raise TypeError
|
|
143
|
+
|
|
144
|
+
def verify(code: CodeObject) -> None:
|
|
145
|
+
"""Reject malformed programs before the VM executes any instruction."""
|
|
146
|
+
if not isinstance(code.name,str) or not isinstance(code.parameters,list) or not all(isinstance(x,str) for x in code.parameters):
|
|
147
|
+
raise BytecodeError("잘못된 HBC 코드 객체입니다.")
|
|
148
|
+
if len(code.instructions)>MAX_INSTRUCTIONS: raise BytecodeError("HBC 명령어 제한을 초과합니다.")
|
|
149
|
+
if code.lines and (len(code.lines)!=len(code.instructions) or not all(isinstance(line,int) and line>=0 for line in code.lines)):
|
|
150
|
+
raise BytecodeError("HBC 소스 위치 테이블 오류")
|
|
151
|
+
count=len(code.instructions)
|
|
152
|
+
for index, ins in enumerate(code.instructions):
|
|
153
|
+
if not isinstance(ins,list) or not ins or not isinstance(ins[0],str):
|
|
154
|
+
raise BytecodeError(f"잘못된 명령어: {index}")
|
|
155
|
+
op=ins[0]; args=ins[1:]
|
|
156
|
+
if op in _NO_ARG:
|
|
157
|
+
if args: raise BytecodeError(f"{op} 명령어 인자 오류")
|
|
158
|
+
elif op in _NAME_ARG:
|
|
159
|
+
if len(args)!=1 or not isinstance(args[0],str): raise BytecodeError(f"{op} 이름 인자 오류")
|
|
160
|
+
elif op in _INT_ARG:
|
|
161
|
+
if len(args)!=1 or not isinstance(args[0],int) or args[0]<0: raise BytecodeError(f"{op} 정수 인자 오류")
|
|
162
|
+
elif op=="CONST":
|
|
163
|
+
if len(args)!=1 or not isinstance(args[0],int) or not 0<=args[0]<len(code.constants): raise BytecodeError("CONST 인덱스 오류")
|
|
164
|
+
elif op in ("JUMP","JUMP_FALSE","FOR_ITER","JUMP_IF_FALSE_OR_POP","JUMP_IF_TRUE_OR_POP"):
|
|
165
|
+
if len(args)!=1 or not isinstance(args[0],int) or not 0<=args[0]<=count: raise BytecodeError(f"{op} 점프 범위 오류")
|
|
166
|
+
elif op=="MAKE_FUNCTION":
|
|
167
|
+
if len(args)!=1 or not isinstance(args[0],dict): raise BytecodeError("함수 코드 객체 오류")
|
|
168
|
+
try:
|
|
169
|
+
if not isinstance(args[0]["defaults"],list) or not all(isinstance(x,str) for x in args[0]["defaults"]): raise TypeError
|
|
170
|
+
if not isinstance(args[0].get("annotations",[]),list) or not all(isinstance(x,str) for x in args[0].get("annotations",[])): raise TypeError
|
|
171
|
+
if not isinstance(args[0].get("annotation_codes",{}),dict) or not all(isinstance(name,str) and isinstance(code,dict) for name,code in args[0].get("annotation_codes",{}).items()): raise TypeError
|
|
172
|
+
if not isinstance(args[0].get("annotation_strings",{}),dict) or not all(isinstance(name,str) and isinstance(text,str) for name,text in args[0].get("annotation_strings",{}).items()): raise TypeError
|
|
173
|
+
for annotation_code in args[0].get("annotation_codes",{}).values(): verify(CodeObject.from_dict(annotation_code))
|
|
174
|
+
if not isinstance(args[0].get("type_params",[]),list) or not all(isinstance(x,str) for x in args[0].get("type_params",[])): raise TypeError
|
|
175
|
+
if not isinstance(args[0].get("local_names",[]),list) or not all(isinstance(x,str) for x in args[0].get("local_names",[])): raise TypeError
|
|
176
|
+
if not isinstance(args[0].get("free_names",[]),list) or not all(isinstance(x,str) for x in args[0].get("free_names",[])): raise TypeError
|
|
177
|
+
signature=args[0]["signature"]
|
|
178
|
+
if not isinstance(args[0]["generator"],bool): raise TypeError
|
|
179
|
+
if not isinstance(args[0]["async"],bool): raise TypeError
|
|
180
|
+
if not isinstance(signature["positional"],list) or not isinstance(signature["keyword_only"],list): raise TypeError
|
|
181
|
+
if not isinstance(signature["positional_only"],list): raise TypeError
|
|
182
|
+
if not all(isinstance(x,str) for x in signature["positional"]+signature["positional_only"]+signature["keyword_only"]): raise TypeError
|
|
183
|
+
if signature["vararg"] is not None and not isinstance(signature["vararg"],str): raise TypeError
|
|
184
|
+
if signature["kwarg"] is not None and not isinstance(signature["kwarg"],str): raise TypeError
|
|
185
|
+
verify(CodeObject.from_dict(args[0]["code"]))
|
|
186
|
+
except (KeyError,TypeError) as exc: raise BytecodeError("함수 코드 객체 오류") from exc
|
|
187
|
+
elif op=="IMPORT_FROM":
|
|
188
|
+
if len(args)!=1 or not isinstance(args[0],dict) or not isinstance(args[0].get("module"),str) or not isinstance(args[0].get("name"),str):
|
|
189
|
+
raise BytecodeError("IMPORT_FROM 인자 오류")
|
|
190
|
+
elif op=="ANNOTATE_LAZY":
|
|
191
|
+
if len(args)!=1 or not isinstance(args[0],dict) or not isinstance(args[0].get("name"),str) or not isinstance(args[0].get("text",""),str) or not isinstance(args[0].get("code"),dict): raise BytecodeError("ANNOTATE_LAZY 인자 오류")
|
|
192
|
+
verify(CodeObject.from_dict(args[0]["code"]))
|
|
193
|
+
elif op=="MAKE_CLASS":
|
|
194
|
+
if len(args)!=1 or not isinstance(args[0],dict): raise BytecodeError("중첩 코드 객체 오류")
|
|
195
|
+
try:
|
|
196
|
+
if not isinstance(args[0]["bases"],int) or args[0]["bases"]<0: raise TypeError
|
|
197
|
+
if not isinstance(args[0].get("incremental_arguments",False),bool): raise TypeError
|
|
198
|
+
if not isinstance(args[0].get("base_starred",[False]*args[0]["bases"]),list) or len(args[0].get("base_starred",[False]*args[0]["bases"]))!=args[0]["bases"] or not all(isinstance(x,bool) for x in args[0].get("base_starred",[False]*args[0]["bases"])): raise TypeError
|
|
199
|
+
if not isinstance(args[0].get("keywords",[]),list) or not all(x is None or isinstance(x,str) for x in args[0].get("keywords",[])): raise TypeError
|
|
200
|
+
if not isinstance(args[0].get("type_params",[]),list) or not all(isinstance(x,str) for x in args[0].get("type_params",[])): raise TypeError
|
|
201
|
+
if not isinstance(args[0].get("local_names",[]),list) or not all(isinstance(x,str) for x in args[0].get("local_names",[])): raise TypeError
|
|
202
|
+
verify(CodeObject.from_dict(args[0]["code"]))
|
|
203
|
+
except (KeyError,TypeError) as exc: raise BytecodeError("중첩 코드 객체 오류") from exc
|
|
204
|
+
elif op in ("CALL_EX","CALL_ARG","CLASS_ARG"):
|
|
205
|
+
if len(args)!=1 or not isinstance(args[0],list): raise BytecodeError("CALL_EX 인자 오류")
|
|
206
|
+
descriptors=args[0] if op=="CALL_EX" else [args[0]]
|
|
207
|
+
for descriptor in descriptors:
|
|
208
|
+
allowed=("base","keyword","star","kwstar") if op=="CLASS_ARG" else ("positional","keyword","star","kwstar")
|
|
209
|
+
if not isinstance(descriptor,list) or len(descriptor)!=2 or descriptor[0] not in allowed: raise BytecodeError("CALL_EX 설명자 오류")
|
|
210
|
+
if descriptor[0]=="keyword" and not isinstance(descriptor[1],str): raise BytecodeError("CALL_EX 키워드 오류")
|
|
211
|
+
elif op=="BUILD_UNPACK":
|
|
212
|
+
if len(args)!=1 or not isinstance(args[0],dict): raise BytecodeError("BUILD_UNPACK 인자 오류")
|
|
213
|
+
try:
|
|
214
|
+
if args[0]["kind"] not in ("list","tuple","set"): raise TypeError
|
|
215
|
+
if not isinstance(args[0]["starred"],list) or not all(isinstance(x,bool) for x in args[0]["starred"]): raise TypeError
|
|
216
|
+
except (KeyError,TypeError) as exc: raise BytecodeError("BUILD_UNPACK 인자 오류") from exc
|
|
217
|
+
elif op=="BUILD_DICT_UNPACK":
|
|
218
|
+
if len(args)!=1 or not isinstance(args[0],list) or not all(item in ("pair","unpack") for item in args[0]): raise BytecodeError("BUILD_DICT_UNPACK 인자 오류")
|
|
219
|
+
elif op=="COLLECTION_BEGIN":
|
|
220
|
+
if len(args)!=1 or args[0] not in ("list","tuple","set","dict"): raise BytecodeError("COLLECTION_BEGIN 인자 오류")
|
|
221
|
+
elif op=="COLLECTION_ADD":
|
|
222
|
+
if len(args)!=1 or args[0] not in ("item","star","pair","unpack"): raise BytecodeError("COLLECTION_ADD 인자 오류")
|
|
223
|
+
elif op=="FORMAT_VALUE":
|
|
224
|
+
if len(args)!=1 or not isinstance(args[0],dict) or args[0].get("conversion") not in (None,"r","s","a") or not isinstance(args[0].get("has_spec"),bool): raise BytecodeError("FORMAT_VALUE 인자 오류")
|
|
225
|
+
elif op=="CHAIN_COMPARE":
|
|
226
|
+
if len(args)!=1 or not isinstance(args[0],dict): raise BytecodeError("CHAIN_COMPARE 인자 오류")
|
|
227
|
+
try:
|
|
228
|
+
valid={"==","!=","<","<=",">",">=","in","is","not in","is not"}
|
|
229
|
+
if not args[0]["operators"] or not all(item in valid for item in args[0]["operators"]): raise TypeError
|
|
230
|
+
if len(args[0]["operands"])!=len(args[0]["operators"])+1: raise TypeError
|
|
231
|
+
for item in args[0]["operands"]: verify(CodeObject.from_dict(item))
|
|
232
|
+
except (KeyError,TypeError) as exc: raise BytecodeError("CHAIN_COMPARE 인자 오류") from exc
|
|
233
|
+
elif op=="MAKE_TYPE_ALIAS":
|
|
234
|
+
if len(args)!=1 or not isinstance(args[0],dict): raise BytecodeError("MAKE_TYPE_ALIAS 인자 오류")
|
|
235
|
+
try:
|
|
236
|
+
if not isinstance(args[0]["name"],str): raise TypeError
|
|
237
|
+
if args[0].get("value_text") is not None and not isinstance(args[0]["value_text"],str): raise TypeError
|
|
238
|
+
if not isinstance(args[0]["parameters"],list): raise TypeError
|
|
239
|
+
for item in args[0]["parameters"]:
|
|
240
|
+
if isinstance(item,(list,tuple)) and len(item)==2 and item[0] in ("typevar","typevartuple","paramspec") and isinstance(item[1],str): continue
|
|
241
|
+
if not isinstance(item,dict) or item.get("kind") not in ("typevar","typevartuple","paramspec") or not isinstance(item.get("name"),str): raise TypeError
|
|
242
|
+
for key in ("bound","default"):
|
|
243
|
+
if item.get(key) is not None: verify(CodeObject.from_dict(item[key]))
|
|
244
|
+
for key in ("bound_text","default_text"):
|
|
245
|
+
if item.get(key) is not None and not isinstance(item[key],str): raise TypeError
|
|
246
|
+
verify(CodeObject.from_dict(args[0]["value"]))
|
|
247
|
+
except (KeyError,TypeError) as exc: raise BytecodeError("MAKE_TYPE_ALIAS 인자 오류") from exc
|
|
248
|
+
elif op=="MAKE_TYPE_PARAMETER":
|
|
249
|
+
if len(args)!=1 or not isinstance(args[0],dict) or args[0].get("kind") not in ("typevar","typevartuple","paramspec") or not isinstance(args[0].get("name"),str): raise BytecodeError("MAKE_TYPE_PARAMETER 인자 오류")
|
|
250
|
+
for key in ("bound","default"):
|
|
251
|
+
if args[0].get(key) is not None: verify(CodeObject.from_dict(args[0][key]))
|
|
252
|
+
for key in ("bound_text","default_text"):
|
|
253
|
+
if args[0].get(key) is not None and not isinstance(args[0][key],str): raise BytecodeError("MAKE_TYPE_PARAMETER 인자 오류")
|
|
254
|
+
elif op=="MATCH_PATTERN":
|
|
255
|
+
if len(args)!=1: raise BytecodeError("MATCH_PATTERN 인자 오류")
|
|
256
|
+
try: _verify_pattern(args[0])
|
|
257
|
+
except (KeyError,TypeError) as exc: raise BytecodeError("MATCH_PATTERN 인자 오류") from exc
|
|
258
|
+
elif op=="TRY":
|
|
259
|
+
if len(args)!=1 or not isinstance(args[0],dict): raise BytecodeError("TRY 코드 객체 오류")
|
|
260
|
+
payload=args[0]
|
|
261
|
+
try:
|
|
262
|
+
verify(CodeObject.from_dict(payload["body"]))
|
|
263
|
+
for handler in payload["handlers"]:
|
|
264
|
+
if handler["type"] is not None:
|
|
265
|
+
if not isinstance(handler["type"],dict): raise TypeError
|
|
266
|
+
verify(CodeObject.from_dict(handler["type"]))
|
|
267
|
+
if handler["alias"] is not None and not isinstance(handler["alias"],str): raise TypeError
|
|
268
|
+
if handler.get("alias_scope","local") not in ("local","global","nonlocal"): raise TypeError
|
|
269
|
+
if not isinstance(handler.get("star",False),bool): raise TypeError
|
|
270
|
+
verify(CodeObject.from_dict(handler["code"]))
|
|
271
|
+
for key in ("else","finally"):
|
|
272
|
+
if payload[key] is not None: verify(CodeObject.from_dict(payload[key]))
|
|
273
|
+
for key in ("break_target","continue_target"):
|
|
274
|
+
if payload[key] is not None and (not isinstance(payload[key],int) or not 0<=payload[key]<=len(code.instructions)): raise TypeError
|
|
275
|
+
except (KeyError,TypeError) as exc: raise BytecodeError("TRY 코드 객체 오류") from exc
|
|
276
|
+
elif op=="COMPREHENSION":
|
|
277
|
+
if len(args)!=1 or not isinstance(args[0],dict): raise BytecodeError("컴프리헨션 코드 객체 오류")
|
|
278
|
+
payload=args[0]
|
|
279
|
+
try:
|
|
280
|
+
if payload["kind"] not in ("listcomp","setcomp","dictcomp","generatorexpr") or not payload["clauses"]: raise TypeError
|
|
281
|
+
if not isinstance(payload.get("bindings",[]),list) or not all(isinstance(name,str) for name in payload.get("bindings",[])): raise TypeError
|
|
282
|
+
for clause in payload["clauses"]:
|
|
283
|
+
_verify_target(clause["target"])
|
|
284
|
+
if not isinstance(clause.get("async",False),bool): raise TypeError
|
|
285
|
+
verify(CodeObject.from_dict(clause["iter"]))
|
|
286
|
+
for item in clause["filters"]: verify(CodeObject.from_dict(item))
|
|
287
|
+
keys=("key","value") if payload["kind"]=="dictcomp" else ("element",)
|
|
288
|
+
for key in keys: verify(CodeObject.from_dict(payload[key]))
|
|
289
|
+
except (KeyError,TypeError) as exc: raise BytecodeError("컴프리헨션 코드 객체 오류") from exc
|
|
290
|
+
elif op=="WITH":
|
|
291
|
+
if len(args)!=1 or not isinstance(args[0],dict): raise BytecodeError("WITH 코드 객체 오류")
|
|
292
|
+
try:
|
|
293
|
+
verify(CodeObject.from_dict(args[0]["body"]))
|
|
294
|
+
for manager in args[0]["managers"]:
|
|
295
|
+
if manager["alias"] is not None: _verify_target(manager["alias"])
|
|
296
|
+
verify(CodeObject.from_dict(manager["code"]))
|
|
297
|
+
except (KeyError,TypeError) as exc: raise BytecodeError("WITH 코드 객체 오류") from exc
|
|
298
|
+
elif op in ("ASYNC_FOR","ASYNC_WITH"):
|
|
299
|
+
if len(args)!=1 or not isinstance(args[0],dict): raise BytecodeError(f"{op} 코드 객체 오류")
|
|
300
|
+
try:
|
|
301
|
+
if op=="ASYNC_FOR":
|
|
302
|
+
_verify_target(args[0]["target"])
|
|
303
|
+
verify(CodeObject.from_dict(args[0]["iter"])); verify(CodeObject.from_dict(args[0]["body"]))
|
|
304
|
+
if args[0].get("else") is not None: verify(CodeObject.from_dict(args[0]["else"]))
|
|
305
|
+
else:
|
|
306
|
+
verify(CodeObject.from_dict(args[0]["body"]))
|
|
307
|
+
for manager in args[0]["managers"]:
|
|
308
|
+
if manager.get("alias") is not None: _verify_target(manager["alias"])
|
|
309
|
+
verify(CodeObject.from_dict(manager["code"]))
|
|
310
|
+
except (KeyError,TypeError) as exc: raise BytecodeError(f"{op} 코드 객체 오류") from exc
|
|
311
|
+
else: raise BytecodeError(f"허용되지 않은 HBC 명령어: {op}")
|
|
312
|
+
_verify_stack(code)
|
|
313
|
+
|
|
314
|
+
def _verify_stack(code: CodeObject) -> None:
|
|
315
|
+
"""Follow every reachable branch and reject stack underflow/inconsistent merges."""
|
|
316
|
+
instructions=code.instructions; count=len(instructions)
|
|
317
|
+
pending=[(0,0)]; seen: dict[int,int]={}
|
|
318
|
+
simple_effect={
|
|
319
|
+
"CONST":1,"LOAD":1,"IMPORT":1,"IMPORT_FROM":1,"IMPORT_STAR":-1,"MAKE_TYPE_ALIAS":1,"MAKE_TYPE_PARAMETER":1,"SUPER":1,
|
|
320
|
+
"STORE":-1,"STORE_GLOBAL":-1,"STORE_NONLOCAL":-1,"ANNOTATE":-1,"DELETE":0,"DELETE_GLOBAL":0,"DELETE_NONLOCAL":0,"SAVE_NAME":0,"RESTORE_NAME":0,"POP":-1,"ITER":0,"NOP":0,"FORMAT":0,"GET_ATTR":0,
|
|
321
|
+
"SET_ATTR":-2,"DELETE_ATTR":-1,"GET_ITEM":-1,"SET_ITEM":-3,"DELETE_ITEM":-2,"ASSERT":-2,"DUP":1,"DUP2":2,"BUILD_SLICE":-2,
|
|
322
|
+
"NEG":0,"POS":0,"NOT":0,"INVERT":0,
|
|
323
|
+
"ADD":-1,"SUB":-1,"MUL":-1,"DIV":-1,"FLOORDIV":-1,"MOD":-1,"POW":-1,
|
|
324
|
+
"IADD":-1,"ISUB":-1,"IMUL":-1,"IDIV":-1,"IFLOORDIV":-1,"IMOD":-1,"IPOW":-1,
|
|
325
|
+
"EQ":-1,"NE":-1,"LT":-1,"LE":-1,"GT":-1,"GE":-1,"IN":-1,"IS":-1,"NOT_IN":-1,"IS_NOT":-1,
|
|
326
|
+
"BIT_OR":-1,"BIT_XOR":-1,"BIT_AND":-1,"LSHIFT":-1,"RSHIFT":-1,"MATMUL":-1,
|
|
327
|
+
"IBIT_OR":-1,"IBIT_XOR":-1,"IBIT_AND":-1,"ILSHIFT":-1,"IRSHIFT":-1,"IMATMUL":-1,
|
|
328
|
+
"BOOL_AND":-1,"BOOL_OR":-1,
|
|
329
|
+
"RAISE":-1,"RAISE_FROM":-2,"RERAISE":0,"SIGNAL_RETURN":-1,"SIGNAL_BREAK":0,"SIGNAL_CONTINUE":0,"YIELD":0,"YIELD_FROM":0,"AWAIT":0,"MATCH_PATTERN":0,"TRY":0,"COMPREHENSION":1,"WITH":0,"ASYNC_FOR":0,"ASYNC_WITH":0,"MAKE_INTERPOLATION":-3,"ANNOTATE_LAZY":0,"CALL_BEGIN":1,"CALL_ARG":-1,"CALL_READY":-1,"CLASS_BEGIN":1,"CLASS_ARG":-1,"COLLECTION_BEGIN":1,"COLLECTION_READY":0,
|
|
330
|
+
}
|
|
331
|
+
while pending:
|
|
332
|
+
ip,depth=pending.pop()
|
|
333
|
+
if ip==count: continue
|
|
334
|
+
previous=seen.get(ip)
|
|
335
|
+
if previous is not None:
|
|
336
|
+
if previous!=depth: raise BytecodeError(f"명령어 {ip}의 스택 깊이가 일치하지 않습니다.")
|
|
337
|
+
continue
|
|
338
|
+
seen[ip]=depth; ins=instructions[ip]; op=ins[0]; arg=ins[1] if len(ins)>1 else None
|
|
339
|
+
if op=="RETURN":
|
|
340
|
+
if depth<1: raise BytecodeError(f"명령어 {ip}에서 스택 언더플로")
|
|
341
|
+
continue
|
|
342
|
+
if op=="JUMP": pending.append((arg,depth)); continue
|
|
343
|
+
if op=="JUMP_FALSE":
|
|
344
|
+
if depth<1: raise BytecodeError(f"명령어 {ip}에서 스택 언더플로")
|
|
345
|
+
pending.extend(((arg,depth-1),(ip+1,depth-1))); continue
|
|
346
|
+
if op in ("JUMP_IF_FALSE_OR_POP","JUMP_IF_TRUE_OR_POP"):
|
|
347
|
+
if depth<1: raise BytecodeError(f"명령어 {ip}에서 스택 언더플로")
|
|
348
|
+
pending.extend(((arg,depth),(ip+1,depth-1))); continue
|
|
349
|
+
if op=="FOR_ITER":
|
|
350
|
+
if depth<1: raise BytecodeError(f"명령어 {ip}에서 스택 언더플로")
|
|
351
|
+
pending.extend(((arg,depth-1),(ip+1,depth+1))); continue
|
|
352
|
+
if op=="CALL": effect=-arg
|
|
353
|
+
elif op=="UNPACK": effect=arg-1
|
|
354
|
+
elif op=="UNPACK_EX": effect=((arg>>16)+(arg&0xFFFF)+1)-1
|
|
355
|
+
elif op=="CALL_EX": effect=-len(arg)
|
|
356
|
+
elif op=="MAKE_FUNCTION": effect=1-len(arg["defaults"])-len(arg.get("annotations",[]))-len(arg.get("type_params",[]))
|
|
357
|
+
elif op=="MAKE_CLASS": effect=-len(arg.get("type_params",[])) if arg.get("incremental_arguments") else 1-arg["bases"]-len(arg.get("keywords",[]))-len(arg.get("type_params",[]))
|
|
358
|
+
elif op in ("BUILD_LIST","BUILD_TUPLE","BUILD_SET","BUILD_STRING","BUILD_TEMPLATE"): effect=1-arg
|
|
359
|
+
elif op=="BUILD_DICT": effect=1-(arg*2)
|
|
360
|
+
elif op=="BUILD_UNPACK": effect=1-len(arg["starred"])
|
|
361
|
+
elif op=="BUILD_DICT_UNPACK": effect=1-sum(2 if item=="pair" else 1 for item in arg)
|
|
362
|
+
elif op=="COLLECTION_ADD": effect=-2 if arg=="pair" else -1
|
|
363
|
+
elif op=="FORMAT_VALUE": effect=-1 if arg["has_spec"] else 0
|
|
364
|
+
elif op=="CHAIN_COMPARE": effect=1
|
|
365
|
+
else: effect=simple_effect[op]
|
|
366
|
+
new_depth=depth+effect
|
|
367
|
+
if new_depth<0: raise BytecodeError(f"명령어 {ip}에서 스택 언더플로")
|
|
368
|
+
pending.append((ip+1,new_depth))
|