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);
|
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) {
|
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)));
|
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 { \
|
#define ATTACH_PROPERTY(propName,how,propValue) do { \
|
||||||
KrkToken val_tok = syntheticToken(propValue); \
|
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); \
|
EMIT_OPERAND_OP(how, val_ind); \
|
||||||
KrkToken name_tok = syntheticToken(propName); \
|
KrkToken name_tok = syntheticToken(propName); \
|
||||||
size_t name_ind = identifierConstant(&name_tok); \
|
size_t name_ind = identifierConstant(&name_tok); \
|
||||||
@ -1563,9 +1587,13 @@ static KrkToken classDeclaration(void) {
|
|||||||
|
|
||||||
beginScope();
|
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();
|
declareVariable();
|
||||||
EMIT_OPERAND_OP(OP_CLASS, constInd);
|
EMIT_OPERAND_OP(OP_CLASS, nameInd);
|
||||||
markInitialized();
|
markInitialized();
|
||||||
|
|
||||||
ClassCompiler classCompiler;
|
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