Implement Python's identifier mangling
This commit is contained in:
parent
d00bdda104
commit
9b5ce15bf7
@ -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
39
test/testNameMangling.krk
Normal 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")
|
1
test/testNameMangling.krk.expect
Normal file
1
test/testNameMangling.krk.expect
Normal file
@ -0,0 +1 @@
|
||||
okay
|
Loading…
Reference in New Issue
Block a user