Implement Python's identifier mangling

This commit is contained in:
K. Lange 2022-07-10 13:13:27 +09:00
parent d00bdda104
commit 9b5ce15bf7
3 changed files with 71 additions and 3 deletions

View File

@ -598,7 +598,31 @@ static size_t emitConstant(KrkValue value) {
return krk_writeConstant(currentChunk(), value, parser.previous.line);
}
static int isMangleable(const char * name, size_t len) {
return (len > 2 && name[0] == '_' && name[1] == '_' && name[len-1] != '_' && (len < 4 || name[len-2] != '_'));
}
static ssize_t identifierConstant(KrkToken * name) {
if (currentClass && isMangleable(name->start, name->length)) {
/* Mangle it */
const char * className = currentClass->name.start;
size_t classLength = currentClass->name.length;
while (classLength && *className == '_') {
classLength--;
className++;
}
size_t total = name->length + 2 + classLength;
char * mangled = malloc(total);
snprintf(mangled, total, "_%.*s%.*s",
(int)classLength, className,
(int)name->length, name->start);
return krk_addConstant(currentChunk(), OBJECT_VAL(krk_takeString(mangled, total-1)));
}
return krk_addConstant(currentChunk(), OBJECT_VAL(krk_copyString(name->start, name->length)));
}
static ssize_t nonidentifierTokenConstant(KrkToken * name) {
return krk_addConstant(currentChunk(), OBJECT_VAL(krk_copyString(name->start, name->length)));
}
@ -1545,7 +1569,7 @@ static void classBody(size_t blockWidth) {
#define ATTACH_PROPERTY(propName,how,propValue) do { \
KrkToken val_tok = syntheticToken(propValue); \
size_t val_ind = identifierConstant(&val_tok); \
size_t val_ind = nonidentifierTokenConstant(&val_tok); \
EMIT_OPERAND_OP(how, val_ind); \
KrkToken name_tok = syntheticToken(propName); \
size_t name_ind = identifierConstant(&name_tok); \
@ -1563,9 +1587,13 @@ static KrkToken classDeclaration(void) {
beginScope();
size_t constInd = identifierConstant(&parser.previous);
/* We want to expose the mangled name within the class definition, which then
* becomes available as a nonlocal, but we want to hand the non-mangled name
* to the CLASS instruction so that __name__ is right. */
size_t nameInd = nonidentifierTokenConstant(&parser.previous);
identifierConstant(&parser.previous);
declareVariable();
EMIT_OPERAND_OP(OP_CLASS, constInd);
EMIT_OPERAND_OP(OP_CLASS, nameInd);
markInitialized();
ClassCompiler classCompiler;

39
test/testNameMangling.krk Normal file
View File

@ -0,0 +1,39 @@
class Foo():
def __test(self):
pass
def __test__(self):
pass
def ___(self):
pass
let f = Foo()
let dirResults = dir(f)
# Straightforard name-mangling and non-mangling casses
assert '_Foo__test' in dirResults
assert '__test__' in dirResults
assert '_Foo__test__' not in dirResults
assert '___' in dirResults
assert '_Foo____' not in dirResults
class Baz():
def __init__(self):
class __Inner():
def __test(self):
pass
self.__inner = __Inner
let b = Baz()
assert '_Baz__inner' in dir(b)
# Inner class name should not be mangled on its own
assert b._Baz__inner.__name__ == '__Inner'
# Mangled identifiers should reduce leading underscores from class name to one
assert '_Inner__test' in dir(b._Baz__inner)
print("okay")

View File

@ -0,0 +1 @@
okay