Skip to content

Commit

Permalink
#590 : recognizing dispatch proxy
Browse files Browse the repository at this point in the history
  • Loading branch information
arakov committed May 21, 2024
1 parent b17017c commit 3c8427b
Show file tree
Hide file tree
Showing 12 changed files with 240 additions and 122 deletions.
84 changes: 65 additions & 19 deletions doc/todo.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,31 @@ In development:
------

[development]
### EPIC: elena 6.1 - redux ###
### EPIC: elena 6.2 ###

=== Iteration 23 ===
prom: returning multiple values from the method / function
--------------------------------------
- #620 : add out argument (a special case of Reference<T> template), to indicate the variable must be assigned before the method exit
- #590 : creating a dynamic interface wrapper (the "class" is created in perm heap, clone the interface and fill with redirect statements)
* #590 : functional test
* #590 : vm : CreateProxyType
* #590 : sample : create a dynamic interface wrapper
* rename CreateProxyTypeLA to InjectProxyLA, the input parameter must contain the target object, the function must verify that
the target proxy is marked as proxy dispatcher (); the target type is marked
as weak interface (the output is not specified); the function will create proxy vmt and inject it to the proxy : otherwise it returns 0

* if the strong interface - verify if the output type list matches the interface one - if not return nil
* the api entry must override
* perm alloc must be struct

--------------------------------------
* #590 : when no output type info - only weak interfaces are supported
* #590 : only stateless type can be used
* #590 : proxy dispatcher : contains only a single field and a custom dispatcher
* #590 : recognize weak interface

=== Iteration 24 ===
--------------------------------------
dev: #574,#265,#619,#580
op: github action - create a draft release
Expand All @@ -14,7 +36,7 @@ In development:
exp: generate code templates - e.g. simple console app
ide:vm debugger, #617, linux ide, linux debugger
tools:#618, #620
prom:#635 on reddit;elena in nutshell every 1 week; post an article describing how to use distributed dictionary for test
prom:#635 on reddit;elena in nutshell every 1 week; post an article about attributes (decorate a class, defining which operation is allowed)
port:elenavm / elt for linux, helloworld gui sample (button)
--------------------------------------
- #184 : linux simplest gui
Expand All @@ -27,22 +49,8 @@ In development:
- #637(bt tests (>=4), all bc tests)
- #69
- chat sample
- #590 : creating a dynamic interface wrapper (the "class" is created in perm heap, clone the interface and fill with redirect statements)
* #590 : functional test
* #590 : vm : CreateProxyType
* #590 : sample : create a dynamic interface wrapper
* rename CreateProxyTypeLA to InjectProxyLA, the input parameter must contain the target object, the function must verify that
the target proxy is marked as proxy dispatcher (); the target type is marked
as weak interface (the output is not specified); the function will create proxy vmt and inject it to the proxy : otherwise it returns 0

* the api entry must override
* perm alloc must be struct
* #590 : only stateless type can be used
* #590 : proxy dispatcher : contains only a single field and a custom dispatcher
--------------------------------------
* #590 : output type list - ignore anonymous / proxy classes

=== Iteration 24 ===
=== Iteration 25 ===
--------------------------------------
dev: async programming (instant messagange sample (chat) )
op:
Expand All @@ -54,7 +62,7 @@ In development:
prom:
port:upndown

=== Iteration 25 ===
=== Iteration 26 ===
--------------------------------------
dev:
op:
Expand All @@ -66,7 +74,45 @@ In development:
prom:
port:

=== Iteration 25 ===
### EPIC: elena 6.3 ###

=== Iteration 27 ===
--------------------------------------
dev:
op:
opt:
maint:
exp:
ide:
tools:
prom:
port:

=== Iteration 28 ===
--------------------------------------
dev:
op:
opt:
maint:
exp:
ide:
tools:
prom:
port:

=== Iteration 29 ===
--------------------------------------
dev:
op:
opt:
maint:
exp:
ide:
tools:
prom:
port:

=== Iteration 30 ===
--------------------------------------
dev:
op:
Expand Down
2 changes: 1 addition & 1 deletion elenasrc3/elc/cliconst.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

namespace elena_lang
{
#define ELC_REVISION_NUMBER 0x0006
#define ELC_REVISION_NUMBER 0x0007

#if defined _M_IX86 || _M_X64

Expand Down
74 changes: 69 additions & 5 deletions elenasrc3/elc/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8471,6 +8471,36 @@ void Compiler :: initializeMethod(ClassScope& scope, MethodScope& methodScope, S
}
}

void Compiler :: compileProxyDispatcher(BuildTreeWriter& writer, CodeScope& codeScope, SyntaxNode node)
{
SyntaxNode objNode = node.firstChild(SyntaxKey::DeclarationMask).firstChild();

// NOTE : the redirect target must be a simple variable
assert(objNode == SyntaxKey::Object);

Expression expression(this, codeScope, writer);

ObjectInfo target = expression.compile(objNode, 0, EAttr::None, nullptr);
switch (target.kind) {
// NOTE : the redirect operation must be done without creating a new frame
case ObjectKind::OuterSelf:
case ObjectKind::Outer:
writer.appendNode(BuildKey::Argument);
writer.appendNode(BuildKey::Field, target.reference);
break;
case ObjectKind::OuterField:
writer.appendNode(BuildKey::Argument);
writer.appendNode(BuildKey::Field, target.reference);
writer.appendNode(BuildKey::Field, target.extra);
break;
default:
codeScope.raiseError(errInvalidOperation, node);
break;
}

writer.appendNode(BuildKey::RedirectOp);
}

void Compiler :: compileRedirectDispatcher(BuildTreeWriter& writer, MethodScope& scope, CodeScope& codeScope, SyntaxNode node,
bool withGenerics)
{
Expand Down Expand Up @@ -8546,6 +8576,8 @@ inline bool hasVariadicFunctionDispatcher(Compiler::ClassScope* classScope, bool
void Compiler :: compileDispatcherMethod(BuildTreeWriter& writer, MethodScope& scope, SyntaxNode node,
bool withGenerics, bool withOpenArgGenerics)
{
ClassScope* classScope = Scope::getScope<ClassScope>(scope, Scope::ScopeLevel::Class);

CodeScope codeScope(&scope);

beginMethod(writer, scope, node, BuildKey::Method, false);
Expand All @@ -8558,7 +8590,10 @@ void Compiler :: compileDispatcherMethod(BuildTreeWriter& writer, MethodScope& s
writer.appendNode(BuildKey::Import, current.arg.reference);
break;
case SyntaxKey::Redirect:
compileRedirectDispatcher(writer, scope, codeScope, current, withGenerics);
if (node.existChild(SyntaxKey::ProxyDispatcher)) {
compileProxyDispatcher(writer, codeScope, current);
}
else compileRedirectDispatcher(writer, scope, codeScope, current, withGenerics);
break;
default:
scope.raiseError(errInvalidOperation, node);
Expand All @@ -8568,8 +8603,6 @@ void Compiler :: compileDispatcherMethod(BuildTreeWriter& writer, MethodScope& s
else {
// if it is an implicit dispatcher
if (withGenerics) {
ClassScope* classScope = Scope::getScope<ClassScope>(scope, Scope::ScopeLevel::Class);

// !! temporally
if (withOpenArgGenerics)
scope.raiseError(errInvalidOperation, node);
Expand All @@ -8588,8 +8621,6 @@ void Compiler :: compileDispatcherMethod(BuildTreeWriter& writer, MethodScope& s
else if (withOpenArgGenerics) {
Expression expression(this, codeScope, writer);

ClassScope* classScope = Scope::getScope<ClassScope>(scope, Scope::ScopeLevel::Class);

ref_t mask = VARIADIC_MESSAGE;
bool mixedDispatcher = false;
bool variadicFunction = hasVariadicFunctionDispatcher(classScope, mixedDispatcher);
Expand Down Expand Up @@ -8982,6 +9013,31 @@ void Compiler :: injectInterfaceDispatch(Scope& scope, SyntaxNode node, ref_t pa
}
}

bool Compiler :: isProxy(Scope& scope, SyntaxNode node)
{
SyntaxNode current = node.firstChild();
while (current != SyntaxKey::None) {
switch (current.key) {
case SyntaxKey::Method:
if (current.arg.reference == scope.moduleScope->buildins.dispatch_message) {
SyntaxNode exprNode = current.findChild(SyntaxKey::Redirect).firstChild();
if (exprNode.firstChild() != SyntaxKey::Object || exprNode.firstChild().nextNode() != SyntaxKey::None) {
return false;
}
else current.appendChild(SyntaxKey::ProxyDispatcher);
}
else return false;
break;
default:
return false;
}

current = current.nextNode();
}

return true;
}

void Compiler :: compileNestedClass(BuildTreeWriter& writer, ClassScope& scope, SyntaxNode node, ref_t parentRef)
{
NamespaceScope* ns = Scope::getScope<NamespaceScope>(scope, Scope::ScopeLevel::Namespace);
Expand Down Expand Up @@ -9026,6 +9082,9 @@ void Compiler :: compileNestedClass(BuildTreeWriter& writer, ClassScope& scope,

generateClassDeclaration(scope, node, elNestedClass | elSealed);

// check if the nested class is a proxy class, and inject the required attribute
bool proxy = scope.info.header.parentRef == scope.moduleScope->buildins.superReference && isProxy(scope, node);

scope.save();

BuildNode buildNode = writer.CurrentNode();
Expand All @@ -9044,6 +9103,11 @@ void Compiler :: compileNestedClass(BuildTreeWriter& writer, ClassScope& scope,
// NOTE : it should be called after the code compilation to take into consideration outer fields
_logic->tweakClassFlags(*scope.moduleScope, scope.reference, scope.info, scope.isClassClass());

// validate the proxy class and set the flag
if (proxy && !_logic->validateDispatcherType(scope.info)) {
scope.raiseError(errInvalidOperation, node);
}

// NOTE : compile once again only auto generated methods
compileVMT(nestedWriter, scope, node, true, false);

Expand Down
3 changes: 3 additions & 0 deletions elenasrc3/elc/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -1411,6 +1411,8 @@ namespace elena_lang

static bool isClassClassOperation(Scope& scope, ObjectInfo target);

static bool isProxy(Scope& scope, SyntaxNode node);

ref_t declareMultiType(Scope& scope, SyntaxNode& node, ref_t elementRef);

void declareClassAttributes(ClassScope& scope, SyntaxNode node, ref_t& fldeclaredFlagsags);
Expand Down Expand Up @@ -1577,6 +1579,7 @@ namespace elena_lang

void compileRedirectDispatcher(BuildTreeWriter& writer, MethodScope& scope, CodeScope& codeScope, SyntaxNode node,
bool withGenerics);
void compileProxyDispatcher(BuildTreeWriter& writer, CodeScope& codeScope, SyntaxNode node);

ObjectInfo compileResendCode(BuildTreeWriter& writer, CodeScope& codeScope, ObjectInfo source, SyntaxNode node);
ObjectInfo compileRedirect(BuildTreeWriter& writer, CodeScope& codeScope, SyntaxNode node, ref_t outputRef);
Expand Down
16 changes: 15 additions & 1 deletion elenasrc3/elc/compilerlogic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2946,4 +2946,18 @@ pos_t CompilerLogic :: definePadding(ModuleScopeBase& scope, pos_t offset, pos_t
default:
return align(offset, scope.ptrSize) - offset;
}
}
}

bool CompilerLogic :: validateDispatcherType(ClassInfo& classInfo)
{
bool isProxy = classInfo.fields.count() == 1 && test(classInfo.header.flags, elWithCustomDispatcher | elNestedClass | elSealed)
&& !testany(classInfo.header.flags, elWithGenerics | elWithVariadics | elWithYieldable | elStructure);

if (isProxy && (classInfo.header.flags & elDebugMask) == 0) {
classInfo.header.flags |= elProxy;

return true;
}

return false;
}
2 changes: 2 additions & 0 deletions elenasrc3/elc/compilerlogic.h
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,8 @@ namespace elena_lang
bool isNeedVerification(ClassInfo& info, VirtualMethodList& implicitMultimethods);
bool verifyMultimethod(ModuleScopeBase& scope, ClassInfo& info, mssg_t message);

bool validateDispatcherType(ClassInfo& classInfo);

mssg_t resolveMultimethod(ModuleScopeBase& scope, mssg_t weakMessage, ref_t targetRef,
ref_t implicitSignatureRef, int& stackSafeAttr, bool selfCall);

Expand Down
2 changes: 2 additions & 0 deletions elenasrc3/engine/elenaconst.h
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ namespace elena_lang
constexpr ref_t elAbstract = 0x00000200;
constexpr ref_t elNoCustomDispatcher = 0x00000400;
constexpr ref_t elStructureRole = 0x00000838;
constexpr ref_t elStructure = 0x00000800;
constexpr ref_t elReadOnlyRole = 0x00001000;
constexpr ref_t elNonStructureRole = 0x00002000;
constexpr ref_t elWrapper = 0x00004000;
Expand Down Expand Up @@ -221,6 +222,7 @@ namespace elena_lang
constexpr ref_t elDebugArray = 0x00070000;
constexpr ref_t elDebugFLOAT64S = 0x00080000;
constexpr ref_t elDebugBytes = 0x00090000;
constexpr ref_t elProxy = 0x000A0000;

// --- LoadResult enum ---
enum class LoadResult
Expand Down
8 changes: 5 additions & 3 deletions elenasrc3/engine/jitlinker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -812,9 +812,11 @@ addr_t JITLinker :: createVMTSection(ReferenceInfo referenceInfo, ClassSectionIn
MemoryBase* codeImage = _imageProvider->getTargetSection(mskCodeRef);
MemoryWriter vmtWriter(vmtImage);

bool withOutputList = _withOutputList && !test(header.flags, elNestedClass);

// allocate space and make VTM offset
_compiler->allocateVMT(vmtWriter, header.flags, header.count,
header.staticSize, _withOutputList);
header.staticSize, withOutputList);

addr_t vaddress = calculateVAddress(vmtWriter, mskRDataRef);

Expand Down Expand Up @@ -862,7 +864,7 @@ addr_t JITLinker :: createVMTSection(ReferenceInfo referenceInfo, ClassSectionIn
_compiler->addVMTEntry(message, methodPosition,
vmtImage->get(position), count);

if (_withOutputList && entry.outputRef && entry.outputRef != sectionInfo.reference) {
if (withOutputList && entry.outputRef && entry.outputRef != sectionInfo.reference) {
// NOTE : the list must contain already resolved message constants, so only type references must be resolved
outputTypeList.add({ message, entry.outputRef });
}
Expand All @@ -889,7 +891,7 @@ addr_t JITLinker :: createVMTSection(ReferenceInfo referenceInfo, ClassSectionIn

resolveClassGlobalAttributes(referenceInfo, vmtReader, vaddress);

addr_t outputListAddress = _withOutputList ?
addr_t outputListAddress = withOutputList ?
resolveOutputTypeList(referenceInfo, outputTypeList) : 0;

// update VMT
Expand Down
1 change: 1 addition & 0 deletions elenasrc3/engine/syntaxtree.h
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ namespace elena_lang
ByRefRetMethod = 0x00010F,
NameArgParameter = 0x000110,
FillingAttr = 0x000111,
ProxyDispatcher = 0x000112,

Column = 0x000201,
Row = 0x000202,
Expand Down
2 changes: 1 addition & 1 deletion elenasrc3/tools/ecv/ecvconst.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

namespace elena_lang
{
#define ECV_REVISION_NUMBER 0x0053
#define ECV_REVISION_NUMBER 0x0001

constexpr auto ECV_GREETING = "ELENA command line ByteCode Viewer %d.%d.%d (C)2021-24 by Aleksey Rakov\n";

Expand Down
3 changes: 3 additions & 0 deletions elenasrc3/tools/ecv/ecviewer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,9 @@ void ByteCodeViewer :: printFlags(ref_t flags, int& row, int pageSize)
case elDebugArray:
printLineAndCount("@flag ", "elDebugArray", row, pageSize);
break;
case elProxy:
printLineAndCount("@flag ", "elProxy", row, pageSize);
break;
default:
break;
}
Expand Down
Loading

0 comments on commit 3c8427b

Please sign in to comment.