diff --git a/.github/workflows/msbuild.yml b/.github/workflows/msbuild.yml index 1b16320f1..68541854d 100644 --- a/.github/workflows/msbuild.yml +++ b/.github/workflows/msbuild.yml @@ -20,7 +20,7 @@ env: # https://docs.github.com/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix BUILD_CONFIGURATION: Release - BUILD_TAG: 6.2.3 + BUILD_TAG: 6.3.0 permissions: contents: read diff --git a/CHANGELOG.md b/CHANGELOG.md index 39f5454c3..bfb97021c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ - [ADDED] method reference - &myMethod - [FIXED] single dispatcher : if an argument is nillable, it can accept nil value - [ADDED] String interpolation + - [REDUX] iterator method + - [ADDED] #496 : private fields - ELC - [FIXED] private constructor must be called directly @@ -18,6 +20,7 @@ - [FIXED] only public classes can be loaded in run-time - [ADDED] #637 : bt optimization 4 unit test - [FIXED] var attribute is allowed to be in the method argument list + - [FIXED] "__typeof self" expression inside the nested class / closure - VM - [FIXED] GC_ALLOC routine for vm mode diff --git a/VERSION b/VERSION index 30bc70ba1..e7e42a4b5 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.2.2 \ No newline at end of file +6.3.0 \ No newline at end of file diff --git a/bin/templates/lib60.cfg b/bin/templates/lib60.cfg index 2407654c8..77327bc06 100644 --- a/bin/templates/lib60.cfg +++ b/bin/templates/lib60.cfg @@ -51,5 +51,6 @@ system'BaseLazyExpression system'Nullable#1 system'UnsafePointer + system'YieldStateEnumerator \ No newline at end of file diff --git a/bin/templates/lib60.config b/bin/templates/lib60.config index 5b322a3ae..fc0e5cadc 100644 --- a/bin/templates/lib60.config +++ b/bin/templates/lib60.config @@ -57,5 +57,6 @@ system'UnsafePointer meta$preloadedSymbols meta$preloadedSymbols + system'YieldStateEnumerator \ No newline at end of file diff --git a/build/aarch64/build_package_arm64.script b/build/aarch64/build_package_arm64.script index 22e50bb81..2960e9061 100755 --- a/build/aarch64/build_package_arm64.script +++ b/build/aarch64/build_package_arm64.script @@ -1,5 +1,5 @@ #!/bin/bash -RELEASE=elena-6.2.3.aarch64-linux +RELEASE=elena-6.3.0.aarch64-linux mkdir -p /usr/share/elena mkdir -p /etc/elena/ diff --git a/build/aarch64/control b/build/aarch64/control index 4724d00d5..3da3ee69a 100644 --- a/build/aarch64/control +++ b/build/aarch64/control @@ -1,5 +1,5 @@ Package: elena-lang -Version: 6.2.3 +Version: 6.3.0 Architecture: aarch64 Maintainer: Alex Rakov Depends: libc6 (>= 2.1) diff --git a/build/amd64/build_package_amd64.script b/build/amd64/build_package_amd64.script index eb0387e0d..53564a43d 100755 --- a/build/amd64/build_package_amd64.script +++ b/build/amd64/build_package_amd64.script @@ -1,5 +1,5 @@ #!/bin/bash -RELEASE=elena-6.2.3.amd64-linux +RELEASE=elena-6.3.0.amd64-linux mkdir -p /usr/share/elena mkdir -p /etc/elena/ diff --git a/build/amd64/control b/build/amd64/control index a8c6cdb32..ebb52c770 100644 --- a/build/amd64/control +++ b/build/amd64/control @@ -1,5 +1,5 @@ Package: elena-lang -Version: 6.2.3 +Version: 6.3.0 Architecture: amd64 Maintainer: Alex Rakov Depends: libc6 (>= 2.1) diff --git a/build/create_package_x64.bat b/build/create_package_x64.bat index a14ca92bb..6874fbd8f 100644 --- a/build/create_package_x64.bat +++ b/build/create_package_x64.bat @@ -73,6 +73,8 @@ copy %~dp0\..\bin\scripts\*.es %~dp0\x64\bin\scripts\ copy %~dp0\..\doc\license %~dp0\x64\doc\ copy %~dp0\..\doc\contributors %~dp0\x64\doc\ copy %~dp0\..\readme.md %~dp0\x64\ +copy %~dp0\..\CHANGELOG.md %~dp0\x64\ +copy %~dp0\..\VERSION %~dp0\x64\ md %~dp0\x64\src60\system xcopy %~dp0\..\src60\system\*.l %~dp0\x64\src60\system /s diff --git a/build/elena_inno.iss b/build/elena_inno.iss index 42afc8118..a2b2b4b36 100644 --- a/build/elena_inno.iss +++ b/build/elena_inno.iss @@ -7,7 +7,7 @@ ; (To generate a new GUID, click Tools | Generate GUID inside the IDE.) AppId={{3CAA69D3-0F98-44B1-A73E-E864BA51D5BD} AppName=ELENA Programming Language -AppVersion=6.2.3 +AppVersion=6.3.0 ;AppVerName=ELENA Programming Language 6.2.0 AppPublisher=Alexey Rakov AppPublisherURL=http://github.com/ELENA-LANG/elena-lang @@ -18,7 +18,7 @@ DefaultGroupName=ELENA Programming Language AllowNoIcons=yes LicenseFile=..\doc\license InfoAfterFile=..\CHANGELOG.md -OutputBaseFilename=elena-lang-6.2.0.x86-setup +OutputBaseFilename=elena-lang-6.3.0.x86-win-setup Compression=lzma SolidCompression=yes ChangesEnvironment=yes diff --git a/build/i386/build_package_i386.script b/build/i386/build_package_i386.script index 74e0924cd..1022969ac 100755 --- a/build/i386/build_package_i386.script +++ b/build/i386/build_package_i386.script @@ -1,5 +1,5 @@ #!/bin/bash -RELEASE=elena-6.2.3.i386-linux +RELEASE=elena-6.3.0.i386-linux mkdir -p /usr/share/elena mkdir -p /etc/elena/ diff --git a/build/i386/control b/build/i386/control index bbb12a2ec..25d579edf 100644 --- a/build/i386/control +++ b/build/i386/control @@ -1,5 +1,5 @@ Package: elena-lang -Version: 6.2.3 +Version: 6.3.0 Architecture: i386 Maintainer: Alex Rakov Depends: libc6 (>= 2.1) diff --git a/build/ppc64le/build_package_ppc64le.script b/build/ppc64le/build_package_ppc64le.script index 10c8c0a2b..0a5715bb5 100755 --- a/build/ppc64le/build_package_ppc64le.script +++ b/build/ppc64le/build_package_ppc64le.script @@ -1,5 +1,5 @@ #!/bin/bash -RELEASE=elena-6.2.3.ppc64le-linux +RELEASE=elena-6.3.0.ppc64le-linux mkdir -p /usr/share/elena mkdir -p /etc/elena/ diff --git a/build/ppc64le/control b/build/ppc64le/control index 0e0690ba1..6c9aeed85 100644 --- a/build/ppc64le/control +++ b/build/ppc64le/control @@ -1,5 +1,5 @@ Package: elena-lang -Version: 6.2.3 +Version: 6.3.0 Architecture: ppc64le Maintainer: Alex Rakov Depends: libc6 (>= 2.1) diff --git a/doc/features b/doc/features index c13bd0250..a41d3b01c 100644 --- a/doc/features +++ b/doc/features @@ -90,3 +90,31 @@ public program() ---------------------------------------------------------------------------- var s := var s := $"a_{ 1 }_b_{ 2 }_c"; + +---------------------------------------------------------------------------- + user-defined literals +---------------------------------------------------------------------------- + + import extensions; + + sealed struct OctalNumber + { + int value; + + int cast() = value; + + constructor(int n) + { + value := n; + } + + cast o(string s) + { + value := s.toInt(8); + } + } + + public program() + { + var n := 12o; + } diff --git a/doc/todo.txt b/doc/todo.txt index b004f9a17..33e02b3e3 100644 --- a/doc/todo.txt +++ b/doc/todo.txt @@ -3,29 +3,58 @@ In development: ------ [development] - ### EPIC: elena 6.3 ### + ### EPIC: elena 6.4 ### - === Iteration 30 (16.8) === + === Iteration 31 === -------------------------------------- dev: + - async programming (general concept, SynchronizationContext, ThreadPool, Task, AsyncLocal, ThreadStatic) - upndown (connector) - - async programming + - chat: add mutex (see dpa_queue) + - #496 gen: - - lpad + - lpad : generate a code based on a record maint: + - #679 port: - - #658 : vscode extension (debug adapter) + - #658 : connect with ldbg from VSCode prom: posting weekly -------------------------------------- - - lpad : generate a code based on a record - - User-defined string literals -------------------------------------- - - #658 : connect with ldbg from VSCode + + === Iteration 32 === + -------------------------------------- + dev: + gen: + maint: + port: + prom: + -------------------------------------- -------------------------------------- - ### EPIC: elena 6.4 ### + === Iteration 33 === + -------------------------------------- + dev: + gen: + maint: + port: + prom: + -------------------------------------- + -------------------------------------- - === Iteration 31 === + === Iteration 34 === + -------------------------------------- + dev: + gen: + maint: + port: + prom: + -------------------------------------- + -------------------------------------- + + ### EPIC: elena 6.5 ### + + === Iteration 35 === -------------------------------------- dev: gen: diff --git a/elenasrc3/common/paths.cpp b/elenasrc3/common/paths.cpp index f22a7aaa5..b8efec428 100644 --- a/elenasrc3/common/paths.cpp +++ b/elenasrc3/common/paths.cpp @@ -72,6 +72,13 @@ bool PathUtil :: checkExtension(path_t path, ustr_t extension) return PathUtil::checkExtension(path, *wExtension); } +void PathUtil :: makeCorrectExePath(PathString& target) +{ + if (!PathUtil::checkExtension(*target, "exe")) { + target.appendExtension("exe"); + } +} + #elif __GNUG__ #include @@ -115,6 +122,11 @@ bool PathUtil :: removeFile(path_t path) { return ::remove(path.str()) != 0;; } + +void PathUtil::makeCorrectExePath(PathString& target) +{ +} + #endif diff --git a/elenasrc3/common/paths.h b/elenasrc3/common/paths.h index b1c24b9a3..3bd3a9bbc 100644 --- a/elenasrc3/common/paths.h +++ b/elenasrc3/common/paths.h @@ -46,6 +46,8 @@ namespace elena_lang #endif static void combineCanonicalized(PathString& target, path_t subpath); + + static void makeCorrectExePath(PathString& target); }; // --- PathString --- diff --git a/elenasrc3/dpa/dpa_queue.h b/elenasrc3/dpa/dpa_queue.h new file mode 100644 index 000000000..b0c655eda --- /dev/null +++ b/elenasrc3/dpa/dpa_queue.h @@ -0,0 +1,67 @@ +//--------------------------------------------------------------------------- +// E L E N A P r o j e c t: ELENA Debugger Adapater +// +// This file contains the DPA queue declarations +// +// (C)2024, by Aleksey Rakov +//--------------------------------------------------------------------------- + +#ifndef DPA_QUEUE_H +#define DPA_QUEUE_H + +#include +#include +#include +#include + +namespace dpa +{ + // --- ContentReader --- + template + class ThreadQueue + { + bool _closed = false; + std::queue _queue; + std::condition_variable _cv; + std::mutex _mutex; + + public: + void close(); + + void put(const T& in); + std::optional take(); + }; + + template + void ThreadQueue :: close() + { + std::unique_lock lock(_mutex); + _closed = true; + _cv.notify_all(); + } + + template + void ThreadQueue :: put(const T& in) + { + std::unique_lock lock(_mutex); + auto notify = _queue.size() == 0 && !_closed; + _queue.push(in); + if (notify) + _cv.notify_all(); + } + + template + std::optional ThreadQueue :: take() + { + std::unique_lock lock(_mutex); + _cv.wait(lock, [&] { return _queue.size() > 0 || _closed; }); + if (_queue.size() == 0) { + return std::optional(); + } + auto out = std::move(_queue.front()); + _queue.pop(); + return std::optional(std::move(out)); + } +} + +#endif \ No newline at end of file diff --git a/elenasrc3/dpa/dpa_session.cpp b/elenasrc3/dpa/dpa_session.cpp index fbab19d18..1acfdf213 100644 --- a/elenasrc3/dpa/dpa_session.cpp +++ b/elenasrc3/dpa/dpa_session.cpp @@ -17,5 +17,43 @@ Session :: Session() Session :: ~Session() { + _inbox.close(); + if (_recvThread.joinable()) { + _recvThread.join(); + } + if (_dispatchThread.joinable()) { + _dispatchThread.join(); + } + _reader.close(); +} + +void Session :: connect() +{ + _reader = ContentReader(); +} + +Payload Session :: getPayload() +{ + return {}; +} + +void Session :: start(const ClosedHandler& onClose) +{ + _recvThread = std::thread([this/*, onClose*/] { + while (_reader.isOpen()) { + if (auto payload = getPayload()) { + _inbox.put(std::move(payload)); + } + } + //if (onClose) { + // onClose(); + //} + }); + + _dispatchThread = std::thread([this] { + while (auto payload = _inbox.take()) { + payload.value()(); + } + }); } \ No newline at end of file diff --git a/elenasrc3/dpa/dpa_session.h b/elenasrc3/dpa/dpa_session.h index 9e758287a..4c0dd35d1 100644 --- a/elenasrc3/dpa/dpa_session.h +++ b/elenasrc3/dpa/dpa_session.h @@ -9,12 +9,40 @@ #ifndef DPA_SESSION_H #define DPA_SESSION_H +#include +#include + +#include "dpa_stream.h" +#include "dpa_queue.h" + namespace dpa { + using Payload = std::function; + using PayloadQueue = ThreadQueue; + + using ClosedHandler = std::function; + + //using RequestHandler = + // std::function; + // --- Session --- class Session { + ContentReader _reader; + PayloadQueue _inbox; + + std::thread _recvThread; + std::thread _dispatchThread; + + Payload getPayload(); + public: + //virtual void registerHandler(const TypeInfo* typeinfo, + // const RequestHandler& handler); + + void connect(); + void start(const ClosedHandler& onClose = {}); + Session(); virtual ~Session(); }; diff --git a/elenasrc3/dpa/dpa_stream.cpp b/elenasrc3/dpa/dpa_stream.cpp new file mode 100644 index 000000000..7401e3459 --- /dev/null +++ b/elenasrc3/dpa/dpa_stream.cpp @@ -0,0 +1,26 @@ +//--------------------------------------------------------------------------- +// E L E N A P r o j e c t: ELENA Debugger Adapater +// +// This file contains the DPA I/O class implementations +// +// (C)2024, by Aleksey Rakov +//--------------------------------------------------------------------------- + +#include "dpa_stream.h" + +using namespace dpa; + +ContentReader :: ~ContentReader() +{ + +} + +bool ContentReader :: isOpen() +{ + return false; +} + +void ContentReader :: close() +{ + +} diff --git a/elenasrc3/dpa/dpa_stream.h b/elenasrc3/dpa/dpa_stream.h new file mode 100644 index 000000000..99e7dc97f --- /dev/null +++ b/elenasrc3/dpa/dpa_stream.h @@ -0,0 +1,31 @@ +//--------------------------------------------------------------------------- +// E L E N A P r o j e c t: ELENA Debugger Adapater +// +// This file contains the DPA I/O class declarations +// +// (C)2024, by Aleksey Rakov +//--------------------------------------------------------------------------- + +#ifndef DPA_STREAM_H +#define DPA_STREAM_H + +namespace dpa +{ + //using RequestHandler = + // std::function; + + // --- ContentReader --- + class ContentReader + { + + public: + bool isOpen(); + + void close(); + + ContentReader() = default; + virtual ~ContentReader(); + }; +} + +#endif \ No newline at end of file diff --git a/elenasrc3/elc/clicommon.h b/elenasrc3/elc/clicommon.h index b2c5c8d77..7c6a785e0 100644 --- a/elenasrc3/elc/clicommon.h +++ b/elenasrc3/elc/clicommon.h @@ -186,6 +186,7 @@ struct BuiltinReferences ref_t nullableTemplateReference; ref_t argArrayTemplateReference; ref_t closureTemplateReference, tupleTemplateReference; + ref_t yielditTemplateReference; ref_t lazyExpressionReference; ref_t pointerReference; @@ -222,6 +223,7 @@ struct BuiltinReferences arrayTemplateReference = argArrayTemplateReference = 0; nullableTemplateReference = 0; closureTemplateReference = lazyExpressionReference = tupleTemplateReference = 0; + yielditTemplateReference = 0; pointerReference = 0; dispatch_message = constructor_message = 0; @@ -489,6 +491,7 @@ struct FieldAttributes bool fieldArray; bool overrideMode; bool autogenerated; + bool privateOne; }; // --- CompilerBase --- diff --git a/elenasrc3/elc/cliconst.h b/elenasrc3/elc/cliconst.h index 8cb3ffffd..ec5141cf2 100644 --- a/elenasrc3/elc/cliconst.h +++ b/elenasrc3/elc/cliconst.h @@ -13,7 +13,7 @@ namespace elena_lang { - #define ELC_REVISION_NUMBER 0x002B + #define ELC_REVISION_NUMBER 0x0030 #if defined _M_IX86 || _M_X64 diff --git a/elenasrc3/elc/compiler.cpp b/elenasrc3/elc/compiler.cpp index 5c8085648..04335a1da 100644 --- a/elenasrc3/elc/compiler.cpp +++ b/elenasrc3/elc/compiler.cpp @@ -1,5 +1,4 @@ //--------------------------------------------------------------------------- -//--------------------------------------------------------------------------- // E L E N A P r o j e c t: ELENA Compiler // // This file contains ELENA compiler class implementation. @@ -1190,13 +1189,14 @@ Compiler::ClassScope :: ClassScope(Scope* ns, ref_t reference, Visibility visibi extensionClassRef = 0; abstractMode = abstractBasedMode = false; extensionDispatcher = false; + withPrivateField = false; } inline ObjectInfo mapClassInfoField(ClassInfo& info, ustr_t identifier, ExpressionAttribute attr, bool ignoreFields) { auto fieldInfo = info.fields.get(identifier); if (!ignoreFields && fieldInfo.offset >= 0) { - bool readOnly = (test(info.header.flags, elReadOnlyRole) || fieldInfo.readOnly) + bool readOnly = (test(info.header.flags, elReadOnlyRole) || FieldInfo::checkHint(fieldInfo, FieldHint::ReadOnly)) && !EAttrs::test(attr, EAttr::InitializerScope); if (test(info.header.flags, elStructureRole)) { @@ -1207,7 +1207,7 @@ inline ObjectInfo mapClassInfoField(ClassInfo& info, ustr_t identifier, Expressi fieldInfo.typeInfo, fieldInfo.offset }; } else if (!ignoreFields && fieldInfo.offset == -2) { - bool readOnly = (test(info.header.flags, elReadOnlyRole) || fieldInfo.readOnly) + bool readOnly = (test(info.header.flags, elReadOnlyRole) || FieldInfo::checkHint(fieldInfo, FieldHint::ReadOnly)) && !EAttrs::test(attr, EAttr::InitializerScope); return { readOnly ? ObjectKind::ReadOnlySelfLocal : ObjectKind::SelfLocal, fieldInfo.typeInfo, 1u, TargetMode::ArrayContent }; @@ -1243,7 +1243,23 @@ ObjectInfo Compiler::ClassScope :: mapField(ustr_t identifier, ExpressionAttribu return mapClassInfoField(targetInfo, identifier, attr, true); } - else return mapClassInfoField(info, identifier, attr, false); + else { + if (withPrivateField) { + auto retVal = mapPrivateField(identifier, attr); + if (retVal.kind != ObjectKind::Unknown) + return retVal; + } + + return mapClassInfoField(info, identifier, attr, false); + } +} + +ObjectInfo Compiler::ClassScope :: mapPrivateField(ustr_t identifier, ExpressionAttribute attr) +{ + IdentifierString privateName(identifier, "$"); + privateName.appendInt(info.inheritLevel); + + return mapClassInfoField(info, *privateName, attr, false); } ObjectInfo Compiler::ClassScope :: mapIdentifier(ustr_t identifier, bool referenceOne, ExpressionAttribute attr) @@ -1419,7 +1435,10 @@ ObjectInfo Compiler::MethodScope :: mapIdentifier(ustr_t identifier, bool refere return mapSelf(false); } else if ((functionMode && !constructorMode) || closureMode || nestedMode) { - return parent->mapIdentifier(OWNER_VAR, false, attr); + if (EAttrs::test(attr, EAttr::RetrievingType)) { + return mapClassSymbol(*this, getClassRef()); + } + else return parent->mapIdentifier(OWNER_VAR, false, attr); } else return mapSelf(); } @@ -1703,7 +1722,7 @@ Compiler::InlineClassScope::Outer Compiler::InlineClassScope :: mapSelf() } outers.add(*moduleScope->selfVar, ownerVar); - mapNewField(info.fields, *moduleScope->selfVar, FieldInfo{ (int)ownerVar.reference, ownerVar.outerObject.typeInfo }); + mapNewField(info.fields, *moduleScope->selfVar, FieldInfo{ (int)ownerVar.reference, ownerVar.outerObject.typeInfo, false, false }); } return ownerVar; @@ -1836,6 +1855,19 @@ bool Compiler::InlineClassScope :: markAsPresaved(ObjectInfo object) return false; } +// --- Compiler::StatemachineClassScope --- + +Compiler::StatemachineClassScope :: StatemachineClassScope(ExprScope* owner, ref_t reference) + : InlineClassScope(owner, reference) +{ + contextSize = 0; +} + +ObjectInfo Compiler::StatemachineClassScope :: mapCurrentField() +{ + return { ObjectKind::Field, { typeRef }, 1 }; +} + // --- Compiler --- Compiler :: Compiler( @@ -2279,7 +2311,7 @@ void Compiler :: declareDictionary(Scope& scope, SyntaxNode node, Visibility vis } void Compiler :: declareVMT(ClassScope& scope, SyntaxNode node, bool& withConstructors, bool& withDefaultConstructor, - bool& yieldMethodNotAllowed, bool staticNotAllowed, bool templateBased) + bool yieldMethodNotAllowed, bool staticNotAllowed, bool templateBased) { SyntaxNode current = node.firstChild(); while (current != SyntaxKey::None) { @@ -2312,15 +2344,8 @@ void Compiler :: declareVMT(ClassScope& scope, SyntaxNode node, bool& withConstr } else methodScope.message = current.arg.reference; - if (methodScope.isYieldable()) { - if (yieldMethodNotAllowed) { - scope.raiseError(errIllegalMethod, current); - } - else yieldMethodNotAllowed = true; - } - declareMethodMetaInfo(methodScope, current); - declareMethod(methodScope, current, scope.abstractMode, staticNotAllowed); + declareMethod(methodScope, current, scope.abstractMode, staticNotAllowed, yieldMethodNotAllowed); if (methodScope.checkHint(MethodHint::Constructor)) { withConstructors = true; @@ -2435,6 +2460,7 @@ Compiler::InheritResult Compiler :: inheritClass(ClassScope& scope, ref_t parent scope.info.header.classRef = classClassCopy; scope.info.header.flags |= flagCopy; + scope.info.inheritLevel++; return InheritResult::irSuccessfull; } @@ -3198,6 +3224,7 @@ bool Compiler :: generateClassField(ClassScope& scope, FieldAttributes& attrs, u int offset = 0; bool embeddable = attrs.isEmbeddable; bool readOnly = attrs.isReadonly; + bool privateOne = attrs.privateOne; ref_t flags = scope.info.header.flags; // a role / interface cannot have fields @@ -3255,7 +3282,7 @@ bool Compiler :: generateClassField(ClassScope& scope, FieldAttributes& attrs, u typeInfo.typeRef = _logic->definePrimitiveArray(*scope.moduleScope, typeInfo.elementRef, test(scope.info.header.flags, elStructureRole)); - scope.info.fields.add(name, { -2, typeInfo, readOnly }); + scope.info.fields.add(name, { -2, typeInfo, readOnly, privateOne }); } else return false; } @@ -3266,9 +3293,6 @@ bool Compiler :: generateClassField(ClassScope& scope, FieldAttributes& attrs, u // if it is a structure field if (test(scope.info.header.flags, elStructureRole)) { - if (test(scope.info.header.flags, elWithYieldable)) - return false; - if (sizeInfo.size <= 0) return false; @@ -3282,7 +3306,7 @@ bool Compiler :: generateClassField(ClassScope& scope, FieldAttributes& attrs, u offset = scope.info.size; scope.info.size += sizeInfo.size; - scope.info.fields.add(name, { offset, typeInfo, readOnly }); + scope.info.fields.add(name, { offset, typeInfo, readOnly, privateOne }); if (typeInfo.isPrimitive()) _logic->tweakPrimitiveClassFlags(scope.info, typeInfo.typeRef); @@ -3295,14 +3319,14 @@ bool Compiler :: generateClassField(ClassScope& scope, FieldAttributes& attrs, u scope.info.header.flags |= elNonStructureRole; offset = scope.info.fields.count(); - scope.info.fields.add(name, { offset, typeInfo, readOnly }); + scope.info.fields.add(name, { offset, typeInfo, readOnly, privateOne }); } } return true; } -void Compiler :: generateClassField(ClassScope& scope, SyntaxNode node, +DeclResult Compiler :: checkAndGenerateClassField(ClassScope& scope, SyntaxNode node, ustr_t name, FieldAttributes& attrs, bool singleField) { TypeInfo typeInfo = attrs.typeInfo; @@ -3315,30 +3339,28 @@ void Compiler :: generateClassField(ClassScope& scope, SyntaxNode node, else if (!test(scope.info.header.flags, elStructureRole)) { typeInfo.typeRef = resolveArrayTemplate(*scope.moduleScope, attrs.typeInfo.typeRef, true); } - else scope.raiseError(errIllegalField, node); + else return DeclResult::Illegal; sizeHint = 0; } - ustr_t name = node.findChild(SyntaxKey::Name).firstChild(SyntaxKey::TerminalMask).identifier(); if (scope.info.fields.exist(name)) { - if (attrs.autogenerated) { - node.setKey(SyntaxKey::Idle); - } - else scope.raiseError(errDuplicatedField, node); - - return; + return DeclResult::Duplicate; } - if (!generateClassField(scope, attrs, name, sizeHint, typeInfo, singleField)) - { + if (!generateClassField(scope, attrs, name, sizeHint, typeInfo, singleField)) { if (attrs.overrideMode && checkPreviousDeclaration(node, name)) { // override the field type if both declared in the same scope auto it = scope.info.fields.getIt(name); (*it).typeInfo = typeInfo; } - else scope.raiseError(errIllegalField, node); + else return DeclResult::Illegal; } + + if (attrs.privateOne) + scope.withPrivateField = true; + + return DeclResult::Success; } void Compiler :: generateClassFields(ClassScope& scope, SyntaxNode node, bool singleField) @@ -3355,7 +3377,28 @@ void Compiler :: generateClassFields(ClassScope& scope, SyntaxNode node, bool si generateClassStaticField(scope, current, attrs); } else if (!isClassClassMode) { - generateClassField(scope, current, attrs, singleField); + DeclResult result = DeclResult::Success; + if (attrs.privateOne) { + IdentifierString privateName(current.findChild(SyntaxKey::Name).firstChild(SyntaxKey::TerminalMask).identifier(), "$"); + privateName.appendHex(scope.info.inheritLevel); + + result = checkAndGenerateClassField(scope, current, *privateName, attrs, singleField); + } + else result = checkAndGenerateClassField(scope, current, current.findChild(SyntaxKey::Name).firstChild(SyntaxKey::TerminalMask).identifier(), attrs, singleField); + + switch (result) { + case Duplicate: + if (attrs.autogenerated) { + current.setKey(SyntaxKey::Idle); + } + else scope.raiseError(errDuplicatedField, current); + break; + case Illegal: + scope.raiseError(errIllegalField, current); + break; + default: + break; + } } } @@ -3905,7 +3948,17 @@ void Compiler :: declareClosureMessage(MethodScope& methodScope, SyntaxNode node methodScope.message = declareClosureParameters(methodScope, argNode); } -void Compiler :: declareMethod(MethodScope& methodScope, SyntaxNode node, bool abstractMode, bool staticNotAllowed) +void Compiler :: declareIteratorMessage(MethodScope& scope, SyntaxNode node) +{ + ref_t itAction = scope.module->mapAction(NEXT_MESSAGE, 0, false); + scope.message = encodeMessage(itAction, 1, 0); + + scope.info.outputRef = scope.moduleScope->branchingInfo.typeRef; + scope.info.hints |= (ref_t)MethodHint::Yieldable; +} + +void Compiler :: declareMethod(MethodScope& methodScope, SyntaxNode node, bool abstractMode, + bool staticNotAllowed, bool yieldMethodNotAllowed) { if (methodScope.checkHint(MethodHint::Static)) { if (staticNotAllowed) @@ -3942,24 +3995,9 @@ void Compiler :: declareMethod(MethodScope& methodScope, SyntaxNode node, bool a if (methodScope.info.hints) node.appendChild(SyntaxKey::Hints, methodScope.info.hints); - if (methodScope.checkHint(MethodHint::Yieldable)) { - // raise an error if the method has arguments - if (getArgCount(methodScope.message) > 1 || (test(methodScope.message, FUNCTION_MESSAGE) && getArgCount(methodScope.message) > 0)) - methodScope.raiseError(errIllegalMethod, node); - - // raise an error if the class is a struct - // only a single yield method is allowed - - // inject yield context assigning - node.parentNode() - .appendChild(SyntaxKey::AssignOperation) - .appendChild(SyntaxKey::YieldContext, methodScope.message); - - // inject yield context field - node.parentNode() - .appendChild(SyntaxKey::Field) - .appendChild(SyntaxKey::Name) - .appendChild(SyntaxKey::identifier, YIELD_CONTEXT_FIELD); + if (methodScope.checkHint(MethodHint::Yieldable) && yieldMethodNotAllowed) { + // raise an error if the method must be static + methodScope.raiseError(errIllegalMethod, node); } } @@ -5423,6 +5461,51 @@ ref_t Compiler :: resolveTemplate(ModuleScopeBase& moduleScope, ref_t templateRe templateRef, parameters, declarationMode, nullptr); } +ref_t Compiler :: resolveStateMachine(Scope& scope, ref_t templateRef, ref_t elementRef) +{ + IdentifierString smName(scope.module->resolveReference(templateRef)); + + List parameters({}); + // HOTFIX : generate a temporal template to pass the type + SyntaxTree dummyTree; + SyntaxTreeWriter dummyWriter(dummyTree); + dummyWriter.newNode(SyntaxKey::Root); + + dummyWriter.newNode(SyntaxKey::TemplateArg, elementRef); + dummyWriter.newNode(SyntaxKey::Type); + + ustr_t referenceName = scope.moduleScope->module->resolveReference(elementRef); + if (isWeakReference(referenceName)) { + dummyWriter.appendNode(SyntaxKey::reference, referenceName); + } + else dummyWriter.appendNode(SyntaxKey::globalreference, referenceName); + + dummyWriter.closeNode(); + dummyWriter.closeNode(); + + dummyWriter.closeNode(); + + SyntaxNode current = dummyTree.readRoot().firstChild(); + while (current == SyntaxKey::TemplateArg) { + parameters.add(current); + + current = current.nextNode(); + } + + smName.append("#1"); + + ref_t templateReference = 0; + if (isWeakReference(*smName)) { + templateReference = scope.module->mapReference(*smName, true); + } + else templateReference = scope.moduleScope->mapFullReference(*smName, true); + + NamespaceScope* nsScope = Scope::getScope(scope, Scope::ScopeLevel::Namespace); + + return _templateProcessor->generateClassTemplate(*scope.moduleScope, + templateReference, parameters, false, nullptr); +} + ref_t Compiler :: resolveClosure(Scope& scope, mssg_t closureMessage, ref_t outputRef) { ref_t signRef = 0; @@ -6178,37 +6261,33 @@ bool Compiler :: evalInitializers(ClassScope& scope, SyntaxNode node) while (current != SyntaxKey::None) { if (current == SyntaxKey::AssignOperation) { found = true; - SyntaxNode lnode = current.findChild(SyntaxKey::Object, SyntaxKey::YieldContext); - if (lnode == SyntaxKey::YieldContext) { - return false; - } - else { - ObjectInfo target = mapObject(scope, lnode, EAttr::None); - switch (target.kind) { - case ObjectKind::Field: - evalulated = false; - break; - case ObjectKind::ClassConstant: - if (target.reference == INVALID_REF) { - ustr_t fieldName = lnode.firstChild(SyntaxKey::TerminalMask).identifier(); - - if (evalClassConstant(fieldName, - scope, current.firstChild(SyntaxKey::ScopeMask), target)) - { - current.setKey(SyntaxKey::Idle); - } - else scope.raiseError(errInvalidOperation, current); - } - break; - case ObjectKind::StaticField: - if (!current.arg.reference) { - current.setArgumentReference(compileStaticAssigning(scope, current)); + + SyntaxNode lnode = current.findChild(SyntaxKey::Object); + ObjectInfo target = mapObject(scope, lnode, EAttr::None); + switch (target.kind) { + case ObjectKind::Field: + evalulated = false; + break; + case ObjectKind::ClassConstant: + if (target.reference == INVALID_REF) { + ustr_t fieldName = lnode.firstChild(SyntaxKey::TerminalMask).identifier(); + + if (evalClassConstant(fieldName, + scope, current.firstChild(SyntaxKey::ScopeMask), target)) + { + current.setKey(SyntaxKey::Idle); } - break; - default: - evalulated = false; - break; - } + else scope.raiseError(errInvalidOperation, current); + } + break; + case ObjectKind::StaticField: + if (!current.arg.reference) { + current.setArgumentReference(compileStaticAssigning(scope, current)); + } + break; + default: + evalulated = false; + break; } } else if (current == SyntaxKey::AddAssignOperation) { @@ -6255,7 +6334,7 @@ ObjectInfo Compiler :: mapClassSymbol(Scope& scope, ref_t classRef) ClassInfo info; scope.moduleScope->loadClassInfo(info, classRef, true); - classClassRef = info.header.classRef; + classClassRef = test(info.header.flags, elClassClass) ? classRef : info.header.classRef; scope.moduleScope->cachedClassReferences.add(classRef, classClassRef); } @@ -7047,7 +7126,7 @@ ObjectInfo Compiler :: mapTerminal(Scope& scope, SyntaxNode node, TypeInfo decla } else if (newOp || castOp) { if (node.key == SyntaxKey::identifier && EAttrs::testAndExclude(attrs, ExpressionAttribute::RetrievingType)) { - auto varInfo = scope.mapIdentifier(node.identifier(), false, attrs); + auto varInfo = scope.mapIdentifier(node.identifier(), false, attrs | ExpressionAttribute::RetrievingType); if (varInfo.kind != ObjectKind::Unknown) { retVal = { ObjectKind::Class, varInfo.typeInfo, 0u, newOp ? TargetMode::Creating : TargetMode::Casting }; } @@ -7694,12 +7773,6 @@ void Compiler :: warnOnUnassignedParameter(SyntaxNode node, Scope& scope, ustr_t } } -inline void clearYieldContext() -{ - // clearing yield context -// writer.appendNode(BuildKey::SavingStackDump); -} - ObjectInfo Compiler :: mapConstructorTarget(MethodScope& scope) { ObjectInfo classSymbol = mapClassSymbol(scope, scope.getClassRef()); @@ -7726,12 +7799,14 @@ void Compiler :: compileMethodCode(BuildTreeWriter& writer, ClassScope* classSco writer.appendNode(BuildKey::Assigning, scope.selfLocal); if (scope.isYieldable()) { + StatemachineClassScope* smScope = Scope::getScope(*classScope, Scope::ScopeLevel::Statemachine); + Expression expression(this, codeScope, writer); // reserve the place for the next step int offset = allocateLocalAddress(codeScope, sizeof(addr_t), false); - ObjectInfo contextField = classScope->mapField(YIELD_CONTEXT_FIELD, EAttr::None); + ObjectInfo contextField = smScope->mapContextField(); expression.writeObjectInfo(contextField); writer.appendNode(BuildKey::LoadingStackDump); @@ -7750,9 +7825,6 @@ void Compiler :: compileMethodCode(BuildTreeWriter& writer, ClassScope* classSco retVal = compileCode(writer, codeScope, bodyNode, scope.closureMode, !_withDebugInfo); break; case SyntaxKey::ReturnExpression: - if (scope.isYieldable()) { - clearYieldContext(); - } retVal = compileRetExpression(writer, codeScope, bodyNode, EAttr::None); break; case SyntaxKey::ResendDispatch: @@ -7791,8 +7863,13 @@ void Compiler :: compileMethodCode(BuildTreeWriter& writer, ClassScope* classSco } } + if (scope.isYieldable()) { + Expression expression(this, codeScope, writer); + + expression.writeObjectInfo({ ObjectKind::Singleton, { scope.moduleScope->branchingInfo.typeRef }, scope.moduleScope->branchingInfo.falseRef}); + } // if the method returns itself - if (retVal.kind == ObjectKind::Unknown && !codeScope.withRetStatement) { + else if (retVal.kind == ObjectKind::Unknown && !codeScope.withRetStatement) { Expression expression(this, codeScope, writer); // NOTE : extension should re @@ -7806,10 +7883,6 @@ void Compiler :: compileMethodCode(BuildTreeWriter& writer, ClassScope* classSco } } - if (scope.isYieldable()) { - clearYieldContext(); - } - writer.appendNode(BuildKey::CloseFrame); if (scope.checkHint(MethodHint::Constant)) { @@ -7823,28 +7896,6 @@ void Compiler :: compileMethodCode(BuildTreeWriter& writer, ClassScope* classSco } } -void Compiler :: compileYieldInitializing(BuildTreeWriter& writer, CodeScope& scope, SyntaxNode node) -{ - ClassScope* classScope = Scope::getScope(scope, Scope::ScopeLevel::Class); - - ObjectInfo contextField = classScope->mapField(YIELD_CONTEXT_FIELD, EAttr::None); - - Expression expression(this, scope, writer); - - pos_t contextSize = classScope->getMssgAttribute(node.arg.reference, ClassAttribute::YieldContextSize); - - writer.appendNode(BuildKey::NilReference); - writer.appendNode(BuildKey::SavingInStack); - - writer.newNode(BuildKey::CreatingStruct, contextSize); - writer.appendNode(BuildKey::Type, scope.moduleScope->buildins.superReference); - writer.closeNode(); - - writer.appendNode(BuildKey::SetImmediateField, 0); - - expression.compileAssigning(node, contextField, { ObjectKind::Object }, true); -} - void Compiler :: compileInitializerMethod(BuildTreeWriter& writer, MethodScope& scope, SyntaxNode classNode) { beginMethod(writer, scope, classNode, BuildKey::Method, false); @@ -7862,10 +7913,7 @@ void Compiler :: compileInitializerMethod(BuildTreeWriter& writer, MethodScope& SyntaxNode current = classNode.firstChild(); while (current != SyntaxKey::None) { if (current == SyntaxKey::AssignOperation) { - if (current.existChild(SyntaxKey::YieldContext)) { - compileYieldInitializing(writer, codeScope, current.findChild(SyntaxKey::YieldContext)); - } - else compileRootExpression(writer, codeScope, current, EAttr::None); + compileRootExpression(writer, codeScope, current, EAttr::None); } current = current.nextNode(); } @@ -8487,6 +8535,7 @@ void Compiler :: compileMethod(BuildTreeWriter& writer, MethodScope& scope, Synt SyntaxNode current = node.firstChild(SyntaxKey::MemberMask); CodeScope codeScope(&scope); + if (scope.info.byRefHandler && !scope.checkHint(MethodHint::InterfaceDispatcher)) { if (current.key == SyntaxKey::Redirect) { compileByRefRedirectHandler(writer, scope, node, scope.info.byRefHandler); @@ -8504,6 +8553,7 @@ void Compiler :: compileMethod(BuildTreeWriter& writer, MethodScope& scope, Synt return; } } + beginMethod(writer, scope, node, BuildKey::Method, _withDebugInfo); switch (current.key) { @@ -8535,19 +8585,87 @@ void Compiler :: compileMethod(BuildTreeWriter& writer, MethodScope& scope, Synt codeScope.syncStack(&scope); endMethod(writer, scope); - if (scope.isYieldable()) { - classScope->addMssgAttribute(scope.message, ClassAttribute::YieldContextSize, scope.reserved2); - } + if (_trackingUnassigned && current == SyntaxKey::CodeBlock) + checkUnassignedVariables(scope, node); +} - if (_trackingUnassigned && current == SyntaxKey::CodeBlock) { - // warn if the variable was not assigned - for (auto it = scope.parameters.start(); !it.eof(); ++it) { - if ((*it).unassigned) { - warnOnUnassignedParameter(node, scope, it.key()); - } +void Compiler :: checkUnassignedVariables(MethodScope& scope, SyntaxNode node) +{ + // warn if the variable was not assigned + for (auto it = scope.parameters.start(); !it.eof(); ++it) { + if ((*it).unassigned) { + warnOnUnassignedParameter(node, scope, it.key()); } } +} + +ref_t Compiler :: resolveYieldType(Scope& scope, SyntaxNode node) +{ + SyntaxNode current = node.findChild(SyntaxKey::TemplateType); + if (current != SyntaxKey::None) { + auto typeInfo = resolveStrongTypeAttribute(scope, current.findChild(SyntaxKey::TemplateArg), true, false); + + return typeInfo.typeRef; + } + + return scope.moduleScope->buildins.superReference; +} + +void Compiler :: compileYieldMethod(BuildTreeWriter& writer, MethodScope& scope, SyntaxNode node) +{ + CodeScope codeScope(&scope); + Expression expression(this, codeScope, writer); + + // create yield state machine + ref_t nestedRef = scope.moduleScope->mapAnonymous(); + StatemachineClassScope smScope(&expression.scope, nestedRef); + smScope.typeRef = resolveYieldType(scope, node); + + BuildNode buildNode = writer.CurrentNode(); + while (buildNode != BuildKey::Root) + buildNode = buildNode.parentNode(); + + BuildTreeWriter nestedWriter(buildNode); + compileStatemachineClass(nestedWriter, smScope, node); + + beginMethod(writer, scope, node, BuildKey::Method, _withDebugInfo); + + // new stack frame + writer.appendNode(BuildKey::OpenFrame); + + ObjectInfo retVal = { ObjectKind::Object, { nestedRef }, 0 }; + + int preservedClosure = 0; + expression.compileNestedInitializing(smScope, nestedRef, preservedClosure, nullptr); + + retVal = expression.saveToTempLocal(retVal); + + ObjectInfo contextField = smScope.mapContextField(); + contextField.kind = ObjectKind::LocalField; + contextField.extra = contextField.reference; + contextField.argument = retVal.argument; + pos_t contextSize = smScope.contextSize; + + writer.appendNode(BuildKey::NilReference); + writer.appendNode(BuildKey::SavingInStack); + + writer.newNode(BuildKey::CreatingStruct, contextSize); + writer.appendNode(BuildKey::Type, scope.moduleScope->buildins.superReference); + writer.closeNode(); + + writer.appendNode(BuildKey::SetImmediateField, 0); + + expression.compileAssigning(node, contextField, { ObjectKind::Object }, true); + + expression.compileConverting(node, retVal, scope.info.outputRef, + scope.checkHint(MethodHint::Stacksafe)); + + codeScope.syncStack(&scope); + + writer.appendNode(BuildKey::CloseFrame); + + endMethod(writer, scope); } bool Compiler :: isCompatible(Scope& scope, ObjectInfo source, ObjectInfo target, bool resolvePrimitives) @@ -9244,6 +9362,96 @@ void Compiler :: compileClosureClass(BuildTreeWriter& writer, ClassScope& scope, scope.save(); } +inline void mapUninqueField(ClassInfo::FieldMap& fields, IdentifierString& name, FieldInfo info) +{ + size_t pos = name.length(); + int index = 0; + ref_t ref = 0; + while (true) { + name[pos] = 0; + name.appendUInt(index++, 16); + + if (!fields.exist(*name)) { + fields.add(*name, info); + + return; + } + } +} + +void Compiler :: compileIteratorMethod(BuildTreeWriter& writer, MethodScope& scope, SyntaxNode node) +{ + StatemachineClassScope* classScope = Scope::getScope(scope, Scope::ScopeLevel::Statemachine); + + assert(!scope.info.byRefHandler); + + beginMethod(writer, scope, node, BuildKey::Method, false); + + CodeScope codeScope(&scope); + + SyntaxNode current = node.firstChild(SyntaxKey::MemberMask); + switch (current.key) { + case SyntaxKey::CodeBlock: + case SyntaxKey::ReturnExpression: + compileMethodCode(writer, classScope, scope, codeScope, node, false); + break; + default: + break; + } + + codeScope.syncStack(&scope); + + endMethod(writer, scope); + + classScope->contextSize = scope.reserved2; + + if (_trackingUnassigned && current == SyntaxKey::CodeBlock) + checkUnassignedVariables(scope, node); +} + +void Compiler :: compileStatemachineClass(BuildTreeWriter& writer, StatemachineClassScope& scope, SyntaxNode node) +{ + ref_t parentRef = resolveStateMachine(scope, scope.moduleScope->buildins.yielditTemplateReference, scope.typeRef); + + declareClassParent(parentRef, scope, node); + generateClassFlags(scope, elNestedClass | elSealed); + + scope.info.attributes.exclude({ 0, ClassAttribute::RuntimeLoadable }); + + writer.newNode(BuildKey::Class, scope.reference); + + NamespaceScope* ns = Scope::getScope(scope, Scope::ScopeLevel::Namespace); + if (_withDebugInfo) + writer.appendNode(BuildKey::Path, *ns->sourcePath); + + MethodScope methodScope(&scope); + declareIteratorMessage(methodScope, node); + + compileIteratorMethod(writer, methodScope, node); + + // handle the abstract flag + if (test(scope.info.header.flags, elAbstract)) { + scope.abstractBasedMode = true; + scope.info.header.flags &= ~elAbstract; + } + + auto m_it = scope.info.methods.getIt(methodScope.message); + if (!m_it.eof()) { + (*m_it).inherited = true; + (*m_it).hints &= ~(ref_t)MethodHint::Abstract; + (*m_it).hints &= (ref_t)MethodHint::Yieldable; + } + else scope.info.methods.add(methodScope.message, methodScope.info); + + // set flags once again + // 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()); + + writer.closeNode(); + + scope.save(); +} + void Compiler :: compileVMT(BuildTreeWriter& writer, ClassScope& scope, SyntaxNode node, bool exclusiveMode, bool ignoreAutoMultimethod) { @@ -9325,7 +9533,10 @@ void Compiler :: compileClassVMT(BuildTreeWriter& writer, ClassScope& classClass _errorProcessor->info(infoCurrentMethod, *messageName); #endif // FULL_OUTOUT_INFO - compileMethod(writer, methodScope, current); + if (methodScope.isYieldable()) { + compileYieldMethod(writer, methodScope, current); + } + else compileMethod(writer, methodScope, current); break; } @@ -9733,6 +9944,7 @@ void Compiler :: prepare(ModuleScopeBase* moduleScope, ForwardResolverBase* forw moduleScope->buildins.closureTemplateReference = safeMapWeakReference(moduleScope, forwardResolver, CLOSURE_FORWARD); moduleScope->buildins.tupleTemplateReference = safeMapWeakReference(moduleScope, forwardResolver, TUPLE_FORWARD); + moduleScope->buildins.yielditTemplateReference = safeMapWeakReference(moduleScope, forwardResolver, YIELDIT_FORWARD); moduleScope->buildins.lazyExpressionReference = safeMapWeakReference(moduleScope, forwardResolver, LAZY_FORWARD); moduleScope->buildins.uintReference = safeMapReference(moduleScope, forwardResolver, UINT_FORWARD); moduleScope->buildins.pointerReference = safeMapReference(moduleScope, forwardResolver, PTR_FORWARD); @@ -10828,15 +11040,10 @@ void Compiler::Class :: declare(SyntaxNode node) bool withConstructors = false; bool withDefConstructor = false; - bool yieldMethodNotAllowed = test(scope.info.header.flags, elWithYieldable) || test(declaredFlags, elStructureRole); + bool yieldMethodNotAllowed = !test(declaredFlags, elStateless); compiler->declareVMT(scope, node, withConstructors, withDefConstructor, yieldMethodNotAllowed, false, test(declaredFlags, elTemplatebased)); - if (yieldMethodNotAllowed && !test(scope.info.header.flags, elWithYieldable) && !test(declaredFlags, elStructureRole)) { - // HOTFIX : trying to figure out if the yield method was declared inside declareVMT - declaredFlags |= elWithYieldable; - } - // NOTE : generateClassDeclaration should be called for the proper class before a class class one // due to dynamic array implementation (auto-generated default constructor should be removed) compiler->generateClassDeclaration(scope, node, declaredFlags); @@ -10998,6 +11205,18 @@ void Compiler::Class :: load() scope.abstractMode = test(scope.info.header.flags, elAbstract); if (test(scope.info.header.flags, elExtension)) scope.extensionClassRef = scope.getAttribute(ClassAttribute::ExtensionRef); + + // check if the class have private fields + for (auto it = scope.info.fields.start(); !it.eof(); ++it) { + if (FieldInfo::checkHint(*it, FieldHint::Private)) { + IdentifierString postfix("$"); + postfix.appendInt(scope.info.inheritLevel); + if (it.key().endsWith(*postfix)) { + scope.withPrivateField = true; + break; + } + } + } } // --- Compiler::ClassClass --- @@ -11054,6 +11273,9 @@ void Compiler::Method :: compile(BuildTreeWriter& writer, SyntaxNode current) else if (scope.checkHint(MethodHint::Initializer)) { compiler->compileInitializerMethod(writer, scope, current.parentNode()); } + else if (scope.checkHint(MethodHint::Yieldable)) { + compiler->compileYieldMethod(writer, scope, current); + } // if it is a normal method else compiler->compileMethod(writer, scope, current); } @@ -11067,9 +11289,9 @@ void Compiler::Method :: compileConstructor(BuildTreeWriter& writer, SyntaxNode #ifdef FULL_OUTOUT_INFO IdentifierString messageName; - ByteCodeUtil::resolveMessageName(messageName, scope.module, methodScope.message); + ByteCodeUtil::resolveMessageName(messageName, scope.module, scope.message); - _errorProcessor->info(infoCurrentMethod, *messageName); + compiler->_errorProcessor->info(infoCurrentMethod, *messageName); #endif // FULL_OUTOUT_INFO compiler->compileConstructor(writer, scope, classClassScope, current, classScope->isAbstract()); @@ -11757,21 +11979,33 @@ void Compiler::Expression :: compileYieldOperation(SyntaxNode node) { CodeScope* codeScope = Scope::getScope(scope, Scope::ScopeLevel::Code); MethodScope* methodScope = Scope::getScope(scope, Scope::ScopeLevel::Method); - ClassScope* classScope = Scope::getScope(scope, Scope::ScopeLevel::Class); + StatemachineClassScope* smScope = Scope::getScope(scope, Scope::ScopeLevel::Statemachine); if (!methodScope->isYieldable()) scope.raiseError(errInvalidOperation, node); - ObjectInfo contextField = classScope->mapField(YIELD_CONTEXT_FIELD, EAttr::None); + ObjectInfo contextField = smScope->mapContextField(); + ObjectInfo currentField = smScope->mapCurrentField(); writer->newNode(BuildKey::YieldingOp, -scope.moduleScope->ptrSize); writer->newNode(BuildKey::Tape); - writeObjectInfo(contextField, node); + ObjectInfo retVal = compile(node.firstChild(), 0, EAttr::None, nullptr); + + bool nillableOp = false; + if (!compileAssigningOp(currentField, retVal, nillableOp)) + scope.raiseError(errInvalidOperation, node); + + if (nillableOp) + scope.raiseWarning(WARNING_LEVEL_1, wrnReturningNillable, node); + writeObjectInfo(contextField, node); writer->appendNode(BuildKey::SavingStackDump); - compiler->compileRetExpression(*writer, *codeScope, node, EAttr::None); + // returning true + writeObjectInfo({ ObjectKind::Singleton, { scope.moduleScope->branchingInfo.typeRef }, scope.moduleScope->branchingInfo.trueRef }); + + writer->appendNode(BuildKey::goingToEOP); writer->closeNode(); writer->closeNode(); @@ -12900,6 +13134,80 @@ ObjectInfo Compiler::Expression :: compileExternalOp(SyntaxNode node, ref_t exte return { ObjectKind::Extern, retType, 0 }; } + +void Compiler::Expression :: compileNestedInitializing(InlineClassScope& classScope, ref_t nestedRef, int& preservedClosure, + ArgumentsInfo* updatedOuterArgs) +{ + ArgumentsInfo list; + // first pass : box an argument if required + for (auto it = classScope.outers.start(); !it.eof(); ++it) { + ObjectInfo arg = (*it).outerObject; + + arg = boxArgument(arg, false, false, false); + switch (arg.kind) { + case ObjectKind::Field: + case ObjectKind::ReadOnlyField: + case ObjectKind::Outer: + case ObjectKind::OuterField: + case ObjectKind::OuterSelf: + arg = saveToTempLocal(arg); + break; + default: + break; + } + + list.add(arg); + } + + writer->newNode(BuildKey::CreatingClass, classScope.info.fields.count()); + writer->appendNode(BuildKey::Type, nestedRef); + writer->closeNode(); + + if (classScope.outers.count() != classScope.info.fields.count()) { + if (classScope.info.fields.count() != 0) { + writer->appendNode(BuildKey::FillOp, classScope.info.fields.count()); + } + } + + // second pass : fill members + int argIndex = 0; + preservedClosure = 0; + for (auto it = classScope.outers.start(); !it.eof(); ++it) { + ObjectInfo source = (*it).outerObject; + ObjectInfo arg = list[argIndex]; + + auto fieldInfo = classScope.info.fields.get(it.key()); + + switch (arg.kind) { + case ObjectKind::SelfLocal: + case ObjectKind::Local: + case ObjectKind::TempLocal: + case ObjectKind::Param: + writer->appendNode(BuildKey::AssignLocalToStack, arg.reference); + writer->appendNode(BuildKey::SetImmediateField, fieldInfo.offset); + break; + default: + // NOTE : should neve be hit + assert(false); + break; + } + + if (updatedOuterArgs && (*it).updated) { + if (!preservedClosure) { + updatedOuterArgs->add({ ObjectKind::ClosureInfo }); + // reserve place for the closure + preservedClosure = updatedOuterArgs->count_pos(); + updatedOuterArgs->add({ }); + } + + updatedOuterArgs->add({ ObjectKind::MemberInfo, (*it).reference }); + updatedOuterArgs->add(source); + } + + argIndex++; + } +} + ObjectInfo Compiler::Expression :: compileNewArrayOp(SyntaxNode node, ObjectInfo source, ref_t targetRef, ArgumentsInfo& arguments) { ref_t sourceRef = compiler->resolveStrongType(scope, source.typeInfo); @@ -13779,6 +14087,7 @@ bool Compiler::Expression :: compileAssigningOp(ObjectInfo target, ObjectInfo ex bool stackSafe = false; bool fieldMode = false; bool fieldFieldMode = false; + bool localFieldMode = false; bool accMode = false; bool lenRequired = false; @@ -13888,6 +14197,12 @@ bool Compiler::Expression :: compileAssigningOp(ObjectInfo target, ObjectInfo ex break; } + case ObjectKind::LocalField: + localFieldMode = true; + operationType = BuildKey::FieldAssigning; + operand = target.extra; + + break; default: return false; } @@ -13908,6 +14223,10 @@ bool Compiler::Expression :: compileAssigningOp(ObjectInfo target, ObjectInfo ex writer->appendNode(BuildKey::SavingInStack, 0); writeObjectInfo(target); } + else if (localFieldMode) { + writer->appendNode(BuildKey::SavingInStack, 0); + writeObjectInfo({ ObjectKind::Local, target.reference }); + } writer->newNode(operationType, operand); if (size != 0) { @@ -14237,74 +14556,8 @@ ObjectInfo Compiler::Expression :: compileNested(InlineClassScope& classScope, E else { ObjectInfo retVal = { ObjectKind::Object, { nestedRef }, 0 }; - ArgumentsInfo list; - // first pass : box an argument if required - for (auto it = classScope.outers.start(); !it.eof(); ++it) { - ObjectInfo arg = (*it).outerObject; - - arg = boxArgument(arg, false, false, false); - switch (arg.kind) { - case ObjectKind::Field: - case ObjectKind::ReadOnlyField: - case ObjectKind::Outer: - case ObjectKind::OuterField: - case ObjectKind::OuterSelf: - arg = saveToTempLocal(arg); - break; - default: - break; - } - - list.add(arg); - } - - writer->newNode(BuildKey::CreatingClass, classScope.info.fields.count()); - writer->appendNode(BuildKey::Type, nestedRef); - writer->closeNode(); - - if (classScope.outers.count() != classScope.info.fields.count()) { - if (classScope.info.fields.count() != 0) { - writer->appendNode(BuildKey::FillOp, classScope.info.fields.count()); - } - } - - // second pass : fill members - int argIndex = 0; int preservedClosure = 0; - for (auto it = classScope.outers.start(); !it.eof(); ++it) { - ObjectInfo source = (*it).outerObject; - ObjectInfo arg = list[argIndex]; - - auto fieldInfo = classScope.info.fields.get(it.key()); - - switch (arg.kind) { - case ObjectKind::SelfLocal: - case ObjectKind::Local: - case ObjectKind::TempLocal: - case ObjectKind::Param: - writer->appendNode(BuildKey::AssignLocalToStack, arg.reference); - writer->appendNode(BuildKey::SetImmediateField, fieldInfo.offset); - break; - default: - // NOTE : should neve be hit - assert(false); - break; - } - - if (updatedOuterArgs && (*it).updated) { - if (!preservedClosure) { - updatedOuterArgs->add({ ObjectKind::ClosureInfo }); - // reserve place for the closure - preservedClosure = updatedOuterArgs->count_pos(); - updatedOuterArgs->add({ }); - } - - updatedOuterArgs->add({ ObjectKind::MemberInfo, (*it).reference }); - updatedOuterArgs->add(source); - } - - argIndex++; - } + compileNestedInitializing(classScope, nestedRef, preservedClosure, updatedOuterArgs); // call init handler if is available if (classScope.info.methods.exist(scope.moduleScope->buildins.init_message)) { diff --git a/elenasrc3/elc/compiler.h b/elenasrc3/elc/compiler.h index a6dd2646e..80f84f684 100644 --- a/elenasrc3/elc/compiler.h +++ b/elenasrc3/elc/compiler.h @@ -120,6 +120,13 @@ namespace elena_lang Weak, }; + enum DeclResult : int + { + Success = 0, + Duplicate = 1, + Illegal = 2 + }; + struct ObjectInfo { ObjectKind kind; @@ -355,6 +362,7 @@ namespace elena_lang Symbol, Class, OwnerClass, + Statemachine, Method, Field, Code, @@ -614,6 +622,7 @@ namespace elena_lang bool abstractMode; bool abstractBasedMode; bool extensionDispatcher; + bool withPrivateField; Scope* getScope(ScopeLevel level) override { @@ -666,6 +675,7 @@ namespace elena_lang ObjectInfo mapMember(ustr_t identifier) override; virtual ObjectInfo mapField(ustr_t identifier, ExpressionAttribute attr); + ObjectInfo mapPrivateField(ustr_t identifier, ExpressionAttribute attr); ObjectInfo mapIdentifier(ustr_t identifier, bool referenceOne, ExpressionAttribute attr) override; @@ -1031,6 +1041,28 @@ namespace elena_lang InlineClassScope(ExprScope* owner, ref_t reference); }; + struct StatemachineClassScope : InlineClassScope + { + pos_t contextSize; + ref_t typeRef; + + ObjectInfo mapContextField() + { + return { ObjectKind::Field }; + } + ObjectInfo mapCurrentField(); + + Scope* getScope(ScopeLevel level) override + { + if (level == ScopeLevel::Statemachine) { + return this; + } + else return InlineClassScope::getScope(level); + } + + StatemachineClassScope(ExprScope* owner, ref_t reference); + }; + struct MessageResolution { bool resolved; @@ -1310,6 +1342,9 @@ namespace elena_lang ObjectInfo allocateResult(ref_t resultRef); + void compileNestedInitializing(InlineClassScope& classScope, ref_t nestedRef, int& preservedClosure, + ArgumentsInfo* updatedOuterArgs); + void compileYieldOperation(SyntaxNode node); void compileSwitchOperation(SyntaxNode node); @@ -1392,6 +1427,8 @@ namespace elena_lang void saveFrameAttributes(BuildTreeWriter& writer, Scope& scope, pos_t reserved, pos_t reservedN); + ref_t resolveYieldType(Scope& scope, SyntaxNode node); + pos_t saveMetaInfo(ModuleBase* module, ustr_t value, ustr_t postfix); ref_t mapNewTerminal(Scope& scope, ustr_t prefix, SyntaxNode nameNode, ustr_t postfix, Visibility visibility, bool ignoreDuplicates = false); @@ -1432,6 +1469,7 @@ namespace elena_lang ref_t resolveTemplate(ModuleScopeBase& moduleScope, ref_t templateRef, ref_t elementRef, bool declarationMode); ref_t resolveClosure(Scope& scope, mssg_t closureMessage, ref_t outputRef); + ref_t resolveStateMachine(Scope& scope, ref_t templateRef, ref_t stateRef); ref_t resolveWrapperTemplate(ModuleScopeBase& moduleScope, ref_t elementRef, bool declarationMode); ref_t resolveArrayTemplate(ModuleScopeBase& moduleScope, ref_t elementRef, bool declarationMode); //ref_t resolveNullableTemplate(ModuleScopeBase& moduleScope, ustr_t ns, ref_t elementRef, bool declarationMode); @@ -1489,7 +1527,7 @@ namespace elena_lang Scope::ScopeLevel level, bool shareMode); void declareVMT(ClassScope& scope, SyntaxNode node, bool& withConstructors, bool& withDefaultConstructor, - bool& yieldMethodNotAllowed, bool staticNotAllowed, bool templateBased); + bool yieldMethodNotAllowed, bool staticNotAllowed, bool templateBased); void registerTemplateSignature(TemplateScope& scope, SyntaxNode node, IdentifierString& signature); void registerExtensionTemplateMethod(TemplateScope& scope, SyntaxNode& node); @@ -1508,6 +1546,8 @@ namespace elena_lang void checkMethodDuplicates(ClassScope& scope, SyntaxNode node, mssg_t message, mssg_t publicMessage, bool protectedOne, bool internalOne); + void checkUnassignedVariables(MethodScope& scope, SyntaxNode node); + ref_t generateConstant(Scope& scope, ObjectInfo& info, ref_t reference, bool saveScope = true); mssg_t defineOutRefMethod(ClassScope& scope, SyntaxNode node, bool isExtension); @@ -1526,7 +1566,7 @@ namespace elena_lang MethodInfo& methodInfo, bool abstractBased); void generateMethodDeclaration(ClassScope& scope, SyntaxNode node, bool closed, bool hideDuplicate); void generateMethodDeclarations(ClassScope& scope, SyntaxNode node, SyntaxKey methodKey, bool closed); - void generateClassField(ClassScope& scope, SyntaxNode node, FieldAttributes& attrs, bool singleField); + DeclResult checkAndGenerateClassField(ClassScope& scope, SyntaxNode node, ustr_t name, FieldAttributes& attrs, bool singleField); void generateClassStaticField(ClassScope& scope, SyntaxNode node, FieldAttributes& attrs); void generateClassFields(ClassScope& scope, SyntaxNode node, bool singleField); void generateClassDeclaration(ClassScope& scope, SyntaxNode node, ref_t declaredFlags); @@ -1546,6 +1586,7 @@ namespace elena_lang void declareVMTMessage(MethodScope& scope, SyntaxNode node, bool withoutWeakMessages, bool declarationMode); void declareClosureMessage(MethodScope& scope, SyntaxNode node); + void declareIteratorMessage(MethodScope& scope, SyntaxNode node); void initializeMethod(ClassScope& scope, MethodScope& methodScope, SyntaxNode current); @@ -1553,7 +1594,8 @@ namespace elena_lang void declareMetaInfo(Scope& scope, SyntaxNode node); void declareMethodMetaInfo(MethodScope& scope, SyntaxNode node); - void declareMethod(MethodScope& scope, SyntaxNode node, bool abstractMode, bool staticNotAllowed); + void declareMethod(MethodScope& scope, SyntaxNode node, bool abstractMode, + bool staticNotAllowed, bool yieldMethodNotAllowed); void declareSymbol(SymbolScope& scope, SyntaxNode node); @@ -1670,18 +1712,20 @@ namespace elena_lang void compileDispatcherMethod(BuildTreeWriter& writer, MethodScope& scope, SyntaxNode node, bool withGenerics, bool withOpenArgGenerics); - void compileYieldInitializing(BuildTreeWriter& writer, CodeScope& scope, SyntaxNode node); void compileInitializerMethod(BuildTreeWriter& writer, MethodScope& scope, SyntaxNode classNode); void compileStaticInitializerMethod(BuildTreeWriter& writer, ClassScope& scope, SyntaxNode classNode); void compileClosureMethod(BuildTreeWriter& writer, MethodScope& scope, SyntaxNode node); + void compileIteratorMethod(BuildTreeWriter& writer, MethodScope& scope, SyntaxNode node); void compileExpressionMethod(BuildTreeWriter& writer, MethodScope& scope, SyntaxNode node); void compileAbstractMethod(BuildTreeWriter& writer, MethodScope& scope, SyntaxNode node, bool abstractMode); void compileMethod(BuildTreeWriter& writer, MethodScope& scope, SyntaxNode node); + void compileYieldMethod(BuildTreeWriter& writer, MethodScope& scope, SyntaxNode node); void compileConstructor(BuildTreeWriter& writer, MethodScope& scope, ClassScope& classClassScope, SyntaxNode node, bool abstractMode); void compileCustomDispatcher(BuildTreeWriter& writer, ClassScope& scope); void compileNestedClass(BuildTreeWriter& writer, ClassScope& scope, SyntaxNode node, ref_t parentRef); void compileClosureClass(BuildTreeWriter& writer, ClassScope& scope, SyntaxNode node); + void compileStatemachineClass(BuildTreeWriter& writer, StatemachineClassScope& scope, SyntaxNode node); void compileVMT(BuildTreeWriter& writer, ClassScope& scope, SyntaxNode node, bool exclusiveMode = false, bool ignoreAutoMultimethod = false); diff --git a/elenasrc3/elc/compilerlogic.cpp b/elenasrc3/elc/compilerlogic.cpp index 7a0bb22c2..519184bd4 100644 --- a/elenasrc3/elc/compilerlogic.cpp +++ b/elenasrc3/elc/compilerlogic.cpp @@ -904,6 +904,9 @@ bool CompilerLogic :: validateFieldAttribute(ref_t attribute, FieldAttributes& a case V_OVERRIDE: attrs.overrideMode = true; break; + case V_PRIVATE: + attrs.privateOne = true; + break; default: return false; } @@ -3023,7 +3026,7 @@ pos_t CompilerLogic :: definePadding(ModuleScopeBase& scope, pos_t offset, pos_t 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); + && !testany(classInfo.header.flags, elWithGenerics | elWithVariadics | elStructure); if (isProxy && (classInfo.header.flags & elDebugMask) == 0) { classInfo.header.flags |= elProxy; diff --git a/elenasrc3/elena-tests/bt_optimization.cpp b/elenasrc3/elena-tests/bt_optimization.cpp index ae071e9bb..9855ae24e 100644 --- a/elenasrc3/elena-tests/bt_optimization.cpp +++ b/elenasrc3/elena-tests/bt_optimization.cpp @@ -130,7 +130,7 @@ void BTOptimization :: SetUp() afterOptimization = buildTree.readRoot().appendChild(BuildKey::Tape); } -void BTOptimization :: runCompilerTest(bool declareOperators) +void BTOptimization :: runBuildTest(bool declareOperators) { // Arrange ModuleScopeBase* moduleScope = env.createModuleScope(true); diff --git a/elenasrc3/elena-tests/bt_optimization.h b/elenasrc3/elena-tests/bt_optimization.h index 027e85330..48d1b5b85 100644 --- a/elenasrc3/elena-tests/bt_optimization.h +++ b/elenasrc3/elena-tests/bt_optimization.h @@ -32,7 +32,7 @@ namespace elena_lang public: void runBTTest(); - void runCompilerTest(bool declareOperators); + void runBuildTest(bool declareOperators); }; class StructTest : public testing::Test diff --git a/elenasrc3/elena-tests/build_tests.cpp b/elenasrc3/elena-tests/build_tests.cpp new file mode 100644 index 000000000..dec6425ae --- /dev/null +++ b/elenasrc3/elena-tests/build_tests.cpp @@ -0,0 +1,83 @@ +#include "pch.h" +// ------------------------------------------------ +#include "bt_optimization.h" + +#include "compiler.h" + +using namespace elena_lang; + +TEST_F(BTOptimization1_1, BuildTest) +{ + runBuildTest(false); +} + +TEST_F(BTOptimization1_2, BuildTest) +{ + runBuildTest(false); +} + +TEST_F(BTOptimization1_3, BuildTest) +{ + runBuildTest(true); +} + +TEST_F(BTOptimization2, BuildTest) +{ + runBuildTest(false); +} + +TEST_F(BTOptimization4, BuildTest) +{ + runBuildTest(false); +} + +TEST_F(StructAlignment, BuildTest) +{ + runTest(); +} + +TEST_F(PackedStructAlignment, BuildTest) +{ + runTest(); +} + +TEST_F(VariadicRuntimeSingleDispatch, BuildTest) +{ + runTest(); +} + +TEST_F(VariadicCompiletimeSingleDispatch, BuildTest) +{ + runTest(); +} + +TEST_F(CallMethodWithoutTarget, BuildTest) +{ + runTest(false); +} + +TEST_F(CallVariadocMethodWithoutTarget, BuildTest) +{ + runTest(false); +} + +// Test scenario : E.load(new C(), new D()); where: class E { constructor load(params B[] args) {}}, C:B and D:B +TEST_F(VariadicCompiletimeSingleDispatch_WithDifferentArgs, BuildTest) +{ + runTest(); +} + +TEST_F(Lambda_CallingPrivateMethod, BuildTest) +{ + runTest(); +} + +TEST_F(IntAssigningNil, BuildTest) +{ + runTest(true); +} + +TEST_F(NillableIntAssigning, BuildTest) +{ + runTest(false); +} \ No newline at end of file diff --git a/elenasrc3/elena-tests/compile_tests.cpp b/elenasrc3/elena-tests/compile_tests.cpp new file mode 100644 index 000000000..3858e7c39 --- /dev/null +++ b/elenasrc3/elena-tests/compile_tests.cpp @@ -0,0 +1,88 @@ +#include "pch.h" +// ------------------------------------------------ +#include "serializer.h" +#include "bcwriter.h" + +#include "compile_tests.h" +#include "scenario_consts.h" + +using namespace elena_lang; + +// ==== Tests Scenarios === + +constexpr auto PrivateField_Scenario1 = "class (nameattr (identifier \"A\" ())field (attribute -2147467262 ()nameattr (identifier \"_x\" ()))method (nameattr (identifier \"setX\" ())code (expression (assign_operation (object (identifier \"_x\" ())expression (object (integer \"2\" ())))))))class (nameattr 62 (identifier \"B\" ())parent (type (identifier \"A\" ()))method (nameattr (identifier \"setParentX\" ())code (expression (assign_operation (object (identifier \"_x\" ())expression (object (integer \"2\" ())))))))"; + +// --- CompileScenarioTest --- + +void CompileScenarioTest :: SetUp() +{ + CompileTest::SetUp(); +} + +void CompileScenarioTest :: runTest(ref_t targetRef, int exptectedError) +{ + // Arrange + ModuleScopeBase* moduleScope = env.createModuleScope(true); + moduleScope->buildins.superReference = 1; + moduleScope->buildins.intReference = intReference; + moduleScope->buildins.constructor_message = + encodeMessage(moduleScope->module->mapAction(CONSTRUCTOR_MESSAGE, 0, false), + 0, FUNCTION_MESSAGE); + + moduleScope->aliases.add("int", intReference); + + Compiler* compiler = env.createCompiler(); + + BuildTree output; + BuildTreeWriter writer(output); + Compiler::Namespace nsScope(compiler, moduleScope, TestErrorProcessor::getInstance(), nullptr, nullptr); + + // Act + nsScope.declare(declarationNode.firstChild(), true); + + Compiler::Class classHelper(nsScope, targetRef, Visibility::Public); + classHelper.load(); + Compiler::Method methodHelper(classHelper); + + SyntaxNode methodNode = findTargetNode(targetRef); + int catchedError = 0; + try + { + methodHelper.compile(writer, methodNode); + } + catch (TestException& ex) { + catchedError = ex.code; + } + + // Assess + EXPECT_TRUE(exptectedError == catchedError); +} + +SyntaxNode CompileScenarioTest :: findTargetNode(ref_t targetRef) +{ + return findClassNode(targetRef).findChild(SyntaxKey::Method); +} + +SyntaxNode CompileScenarioTest :: findClassNode(ref_t targetRef) +{ + return SyntaxTree::gotoChild(declarationNode.firstChild(), SyntaxKey::Class, targetRef); +} + +// --- CallPrivateConstructorDirectly --- + +void AccessPrivateField :: SetUp() +{ + CompileScenarioTest::SetUp(); + + LoadDeclarationScenario(S_DefaultNamespace_3, S_IntNumber, PrivateField_Scenario1); +} + +TEST_F(AccessPrivateField, AccessPrivateFieldTest) +{ + runTest(3, 0); +} + +TEST_F(AccessPrivateField, AccessParentPrivateFieldTest) +{ + runTest(4, 106); +} diff --git a/elenasrc3/elena-tests/compile_tests.h b/elenasrc3/elena-tests/compile_tests.h new file mode 100644 index 000000000..b68d42f64 --- /dev/null +++ b/elenasrc3/elena-tests/compile_tests.h @@ -0,0 +1,36 @@ +//--------------------------------------------------------------------------- +// E L E N A P r o j e c t: ELENA Compiler +// +// This header contains ELENA Compile Test Fixture declarations +// (C)2024, by Aleksey Rakov +//--------------------------------------------------------------------------- + +#ifndef COMPILE_TESTS_H +#define COMPILE_TESTS_H + +#include "tests_common.h" + +namespace elena_lang +{ + class CompileScenarioTest : public CompileTest + { + protected: + ref_t intReference; + + virtual SyntaxNode findTargetNode(ref_t targetRef); + virtual SyntaxNode findClassNode(ref_t targetRef); + + void SetUp() override; + + public: + void runTest(ref_t targetRef, int exptectedError); + }; + + class AccessPrivateField : public CompileScenarioTest + { + protected: + void SetUp() override; + }; +} + +#endif diff --git a/elenasrc3/elena-tests/compiler_tests.cpp b/elenasrc3/elena-tests/compiler_tests.cpp deleted file mode 100644 index 41c2566db..000000000 --- a/elenasrc3/elena-tests/compiler_tests.cpp +++ /dev/null @@ -1,83 +0,0 @@ -#include "pch.h" -// ------------------------------------------------ -#include "bt_optimization.h" - -#include "compiler.h" - -using namespace elena_lang; - -TEST_F(BTOptimization1_1, CompilerTest) -{ - runCompilerTest(false); -} - -TEST_F(BTOptimization1_2, CompilerTest) -{ - runCompilerTest(false); -} - -TEST_F(BTOptimization1_3, CompilerTest) -{ - runCompilerTest(true); -} - -TEST_F(BTOptimization2, CompilerTest) -{ - runCompilerTest(false); -} - -TEST_F(BTOptimization4, CompilerTest) -{ - runCompilerTest(false); -} - -TEST_F(StructAlignment, CompilerTest) -{ - runTest(); -} - -TEST_F(PackedStructAlignment, CompilerTest) -{ - runTest(); -} - -TEST_F(VariadicRuntimeSingleDispatch, CompilerTest) -{ - runTest(); -} - -TEST_F(VariadicCompiletimeSingleDispatch, CompilerTest) -{ - runTest(); -} - -TEST_F(CallMethodWithoutTarget, CompilerTest) -{ - runTest(false); -} - -TEST_F(CallVariadocMethodWithoutTarget, CompilerTest) -{ - runTest(false); -} - -// Test scenario : E.load(new C(), new D()); where: class E { constructor load(params B[] args) {}}, C:B and D:B -TEST_F(VariadicCompiletimeSingleDispatch_WithDifferentArgs, CompilerTest) -{ - runTest(); -} - -TEST_F(Lambda_CallingPrivateMethod, CompilerTest) -{ - runTest(); -} - -TEST_F(IntAssigningNil, CompilerTest) -{ - runTest(true); -} - -TEST_F(NillableIntAssigning, CompilerTest) -{ - runTest(false); -} \ No newline at end of file diff --git a/elenasrc3/elena-tests/elena-tests.vcxproj b/elenasrc3/elena-tests/elena-tests.vcxproj index 3ce4af8c8..3dbb030e5 100644 --- a/elenasrc3/elena-tests/elena-tests.vcxproj +++ b/elenasrc3/elena-tests/elena-tests.vcxproj @@ -135,6 +135,7 @@ + @@ -158,8 +159,9 @@ - + + diff --git a/elenasrc3/elena-tests/tests_common.cpp b/elenasrc3/elena-tests/tests_common.cpp index fb5df504f..e240f1c4f 100644 --- a/elenasrc3/elena-tests/tests_common.cpp +++ b/elenasrc3/elena-tests/tests_common.cpp @@ -403,6 +403,16 @@ void ScenarioTest::SetUp() controlOutputNode = buildTree.readRoot().appendChild(BuildKey::Tape); } +// --- CompileTest --- + +void CompileTest :: SetUp() +{ + SyntaxTreeWriter writer(syntaxTree); + writer.appendNode(SyntaxKey::Root); + + declarationNode = syntaxTree.readRoot().appendChild(SyntaxKey::Idle, 1); +} + // --- MethodScenarioTest --- void MethodScenarioTest :: runTest(bool withProtectedConstructor, bool withAttributes) diff --git a/elenasrc3/elena-tests/tests_common.h b/elenasrc3/elena-tests/tests_common.h index e8ca1ec8a..cfeef7ebc 100644 --- a/elenasrc3/elena-tests/tests_common.h +++ b/elenasrc3/elena-tests/tests_common.h @@ -234,6 +234,13 @@ namespace elena_lang void SetUp() override; }; + // --- ScenarioTest --- + class CompileTest : public BaseFixture + { + protected: + void SetUp() override; + }; + // --- MethodScenarioTest --- class MethodScenarioTest : public ScenarioTest { diff --git a/elenasrc3/elenavm/elenavmmachine.cpp b/elenasrc3/elenavm/elenavmmachine.cpp index 1d28c3c66..0fd65bde7 100644 --- a/elenasrc3/elenavm/elenavmmachine.cpp +++ b/elenasrc3/elenavm/elenavmmachine.cpp @@ -113,7 +113,7 @@ ELENAVMMachine :: ELENAVMMachine(path_t configPath, PresenterBase* presenter, Pl } ustr_t ELENAVMMachine::getArchitectureName() { -#if defined(_WIN64) || defined(__x86_64__) || defined(__ppc64__) +#if defined(_WIN64) || defined(__x86_64__) || defined(__ppc64__) || defined(__aarch64__) return "64-bit"; #else return "32-bit"; diff --git a/elenasrc3/engine/bytecode.cpp b/elenasrc3/engine/bytecode.cpp index 4fe89ba0f..abe181f55 100644 --- a/elenasrc3/engine/bytecode.cpp +++ b/elenasrc3/engine/bytecode.cpp @@ -313,9 +313,6 @@ void ByteCodeUtil :: generateAutoSymbol(ModuleInfoList& symbolList, ModuleBase* pos_t sizePlaceholder = writer.position(); writer.writePos(0); - pos_t command = 0; - ustr_t strArg = nullptr; - ByteCodeUtil::write(writer, ByteCode::OpenIN, 2, 0); // generate the preloaded list diff --git a/elenasrc3/engine/elena.h b/elenasrc3/engine/elena.h index d545cbeb2..81ed93fe4 100644 --- a/elenasrc3/engine/elena.h +++ b/elenasrc3/engine/elena.h @@ -1093,14 +1093,6 @@ namespace elena_lang } }; - // --- FieldInfo --- - struct FieldInfo - { - int offset; - TypeInfo typeInfo; - bool readOnly; - }; - // --- StaticFieldInfo --- struct StaticFieldInfo { diff --git a/elenasrc3/engine/elenaconst.h b/elenasrc3/engine/elenaconst.h index 1d5faed3e..d60c23020 100644 --- a/elenasrc3/engine/elenaconst.h +++ b/elenasrc3/engine/elenaconst.h @@ -13,7 +13,7 @@ namespace elena_lang { // --- Common ELENA Engine constants --- #define ENGINE_MAJOR_VERSION 6 // ELENA Engine version - #define ENGINE_MINOR_VERSION 3 + #define ENGINE_MINOR_VERSION 4 constexpr auto LINE_LEN = 0x1000; // the maximal source line length constexpr auto IDENTIFIER_LEN = 0x0300; // the maximal identifier length @@ -26,11 +26,11 @@ namespace elena_lang constexpr auto MESSAGE_FLAG_MASK = 0x1E0u; constexpr auto STATIC_MESSAGE = 0x100u; - constexpr auto FUNCTION_MESSAGE = 0x020u; // indicates it is an invoke message (without target variable in the call stack) + constexpr auto FUNCTION_MESSAGE = 0x020u; // indicates it is an invoke message (without target variable in the call stack) constexpr auto CONVERSION_MESSAGE = 0x040u; constexpr auto VARIADIC_MESSAGE = 0x080u; constexpr auto PROPERTY_MESSAGE = 0x0C0u; - constexpr auto PREFIX_MESSAGE_MASK = 0x0C0u; // HOTFIX : is used to correctly identify VARIADIC_MESSAGE or PROPERTY_MESSAGE + constexpr auto PREFIX_MESSAGE_MASK = 0x0C0u; // HOTFIX : is used to correctly identify VARIADIC_MESSAGE or PROPERTY_MESSAGE constexpr auto ARG_COUNT = 0x01Eu; constexpr auto ARG_MASK = 0x01Fu; @@ -38,7 +38,7 @@ namespace elena_lang // --- ELENA Module structure constants --- constexpr auto ELENA_SIGNITURE = "ELENA."; // the stand alone image constexpr auto ELENA_VM_SIGNITURE = "VM.ELENA."; // the stand alone image - constexpr auto MODULE_SIGNATURE = "ELENA.0620"; // the module version + constexpr auto MODULE_SIGNATURE = "ELENA.0604"; // the module version constexpr auto DEBUG_MODULE_SIGNATURE = "ED.06"; // --- ELENA core module names --- @@ -69,7 +69,7 @@ namespace elena_lang constexpr auto TEMPLATE_PREFIX_NS_ENCODED = "@$auto@"; constexpr auto FORWARD_PREFIX_NS = "$forwards'"; constexpr auto AUTO_SYMBOL_PREFIX = "@autosymbol"; - constexpr auto INLINE_CLASSNAME = "$inline"; // nested class generic name + constexpr auto INLINE_CLASSNAME = "$inline"; // nested class generic name constexpr auto OPERATION_MAP_KEY = "statements"; constexpr auto PREDEFINED_MAP_KEY = "defaults"; @@ -110,7 +110,8 @@ namespace elena_lang constexpr auto MESSAGE_NAME_FORWARD = "$subject"; // the message class constexpr auto EXT_MESSAGE_FORWARD = "$ext_message"; // the extension message class constexpr auto CLOSURE_FORWARD = "$closure"; // the closure template class - constexpr auto TUPLE_FORWARD = "$tuple"; // the closure template class + constexpr auto TUPLE_FORWARD = "$tuple"; // the tuple template class + constexpr auto YIELDIT_FORWARD = "$yieldit"; // the yield state machine iterator template class constexpr auto UINT_FORWARD = "$uint"; // the uint wrapper constexpr auto PTR_FORWARD = "$ptr"; // the ptr wrapper constexpr auto LAZY_FORWARD = "$lazy"; @@ -149,35 +150,37 @@ namespace elena_lang constexpr auto TRY_INVOKE_MESSAGE = "#try_invoke"; constexpr auto INIT_MESSAGE = "#init"; - constexpr auto ADD_MESSAGE = "add"; - constexpr auto SUB_MESSAGE = "subtract"; - constexpr auto MUL_MESSAGE = "multiply"; - constexpr auto DIV_MESSAGE = "divide"; - constexpr auto BAND_MESSAGE = "band"; - constexpr auto BOR_MESSAGE = "bor"; - constexpr auto BXOR_MESSAGE = "bxor"; - constexpr auto REFER_MESSAGE = "at"; - constexpr auto SET_REFER_MESSAGE = "setAt"; - constexpr auto IF_MESSAGE = "if"; - constexpr auto IIF_MESSAGE = "iif"; - constexpr auto EQUAL_MESSAGE = "equal"; - constexpr auto NOT_MESSAGE = "Inverted"; - constexpr auto NEGATE_MESSAGE = "Negative"; - constexpr auto VALUE_MESSAGE = "Value"; - constexpr auto ITEM_MESSAGE = "Value"; - constexpr auto DEFAULT_MESSAGE = "Default"; - constexpr auto BNOT_MESSAGE = "BInverted"; - constexpr auto NOTEQUAL_MESSAGE = "notequal"; - constexpr auto LESS_MESSAGE = "less"; - constexpr auto NOTLESS_MESSAGE = "notless"; - constexpr auto GREATER_MESSAGE = "greater"; - constexpr auto NOTGREATER_MESSAGE = "notgreater"; - constexpr auto AND_MESSAGE = "and"; - constexpr auto OR_MESSAGE = "or"; - constexpr auto XOR_MESSAGE = "xor"; - constexpr auto SHL_MESSAGE = "shiftLeft"; - constexpr auto SHR_MESSAGE = "shiftRight"; - + constexpr auto NEXT_MESSAGE = "next"; + constexpr auto CURRENT_FIELD = "__current"; + + constexpr auto ADD_MESSAGE = "add"; + constexpr auto SUB_MESSAGE = "subtract"; + constexpr auto MUL_MESSAGE = "multiply"; + constexpr auto DIV_MESSAGE = "divide"; + constexpr auto BAND_MESSAGE = "band"; + constexpr auto BOR_MESSAGE = "bor"; + constexpr auto BXOR_MESSAGE = "bxor"; + constexpr auto REFER_MESSAGE = "at"; + constexpr auto SET_REFER_MESSAGE = "setAt"; + constexpr auto IF_MESSAGE = "if"; + constexpr auto IIF_MESSAGE = "iif"; + constexpr auto EQUAL_MESSAGE = "equal"; + constexpr auto NOT_MESSAGE = "Inverted"; + constexpr auto NEGATE_MESSAGE = "Negative"; + constexpr auto VALUE_MESSAGE = "Value"; + constexpr auto ITEM_MESSAGE = "Value"; + constexpr auto DEFAULT_MESSAGE = "Default"; + constexpr auto BNOT_MESSAGE = "BInverted"; + constexpr auto NOTEQUAL_MESSAGE = "notequal"; + constexpr auto LESS_MESSAGE = "less"; + constexpr auto NOTLESS_MESSAGE = "notless"; + constexpr auto GREATER_MESSAGE = "greater"; + constexpr auto NOTGREATER_MESSAGE = "notgreater"; + constexpr auto AND_MESSAGE = "and"; + constexpr auto OR_MESSAGE = "or"; + constexpr auto XOR_MESSAGE = "xor"; + constexpr auto SHL_MESSAGE = "shiftLeft"; + constexpr auto SHR_MESSAGE = "shiftRight"; // --- constant string lengths --- constexpr auto TEMPLATE_PREFIX_NS_LEN = 7; @@ -208,7 +211,6 @@ namespace elena_lang constexpr ref_t elMessageName = 0x01000000; constexpr ref_t elWithGenerics = 0x02000000; constexpr ref_t elVirtualVMT = 0x04000000; - constexpr ref_t elWithYieldable = 0x08000000; constexpr ref_t elGroup = 0x10000000; constexpr ref_t elPacked = 0x20000000; constexpr ref_t elTemplatebased = 0x40000000; diff --git a/elenasrc3/engine/jitlinker.cpp b/elenasrc3/engine/jitlinker.cpp index f63e13f34..856703738 100644 --- a/elenasrc3/engine/jitlinker.cpp +++ b/elenasrc3/engine/jitlinker.cpp @@ -805,10 +805,6 @@ addr_t JITLinker :: createVMTSection(ReferenceInfo referenceInfo, ClassSectionIn #ifdef FULL_OUTOUT_INFO if (referenceInfo.referenceName) printf("linking %s\n", referenceInfo.referenceName.str()); - - if (referenceInfo.referenceName.compare("'$inline0")) - referenceInfo.module = referenceInfo.module; - #endif // FULL_OUTOUT_INFO referenceInfo.module = sectionInfo.module; diff --git a/elenasrc3/engine/langcommon.h b/elenasrc3/engine/langcommon.h index 94427dce5..3355dfd62 100644 --- a/elenasrc3/engine/langcommon.h +++ b/elenasrc3/engine/langcommon.h @@ -137,6 +137,42 @@ namespace elena_lang } }; + enum class FieldHint : ref_t + { + None = 0x00000000, + ReadOnly = 0x00000001, + Private = 0x00000002, + }; + + // --- FieldInfo --- + struct FieldInfo + { + int offset; + TypeInfo typeInfo; + ref_t hints; + + static bool checkHint(FieldInfo& info, FieldHint hint) + { + return test(info.hints, (ref_t)hint); + } + + FieldInfo() + : offset(0), typeInfo({}), hints(0) + { + + } + FieldInfo(int offset, TypeInfo typeInfo) + : offset(offset), typeInfo(typeInfo), hints(0) + { + } + FieldInfo(int offset, TypeInfo typeInfo, bool readOnly, bool privateOne) + : offset(offset), typeInfo(typeInfo), hints(0) + { + hints |= readOnly ? (ref_t)FieldHint::ReadOnly : 0; + hints |= privateOne ? (ref_t)FieldHint::Private : 0; + } + }; + // --- ClassInfo --- struct ClassInfo { @@ -144,6 +180,7 @@ namespace elena_lang typedef MemoryMap FieldMap; typedef MemoryMap StaticFieldMap; + pos_t inheritLevel; ClassHeader header; pos_t size; // Object size MethodMap methods; @@ -179,6 +216,7 @@ namespace elena_lang writer->write(&header, sizeof(ClassHeader)); writer->writeDWord(size); if (!headerAndSizeOnly) { + writer->writeDWord(inheritLevel); writer->writePos(fields.count()); fields.forEach(writer, [](StreamWriter* writer, ustr_t name, FieldInfo info) { @@ -209,11 +247,12 @@ namespace elena_lang reader->read(&header, sizeof(ClassHeader)); size = reader->getDWord(); if (!headerAndSizeOnly) { + inheritLevel = reader->getDWord(); pos_t fieldCount = reader->getPos(); for (pos_t i = 0; i < fieldCount; i++) { IdentifierString fieldName; reader->readString(fieldName); - FieldInfo fieldInfo; + FieldInfo fieldInfo = {}; reader->read(&fieldInfo, sizeof(fieldInfo)); fields.add(*fieldName, fieldInfo); @@ -244,17 +283,14 @@ namespace elena_lang } ClassInfo() : + inheritLevel(0), header({}), size(0), methods({}), - fields({ -1 }), + fields({ -1, {} }), statics({ -1 }), attributes(0) { - //header.staticSize = 0; - //header.parentRef = header.classRef = 0; - //header.flags = 0; - //header.count = size = 0; } }; @@ -392,6 +428,7 @@ namespace elena_lang /// accessors: constexpr auto V_GETACCESSOR = 0x80005001u; constexpr auto V_SETACCESSOR = 0x80005002u; + constexpr auto V_YIELDABLE = 0x80005003u; /// visibility: constexpr auto V_PUBLIC = 0x80004001u; @@ -403,7 +440,6 @@ namespace elena_lang constexpr auto V_SEALED = 0x80003001u; constexpr auto V_ABSTRACT = 0x80003002u; constexpr auto V_CLOSED = 0x80003003u; - constexpr auto V_YIELDABLE = 0x80003004u; constexpr auto V_PREDEFINED = 0x80003005u; constexpr auto V_OVERRIDE = 0x80003006u; @@ -652,7 +688,6 @@ namespace elena_lang constexpr auto RETVAL_ARG = "$retVal"; constexpr auto PARENT_VAR = "$parent"; constexpr auto OWNER_VAR = "$owner"; - constexpr auto YIELD_CONTEXT_FIELD = "$context"; inline ustr_t getPlatformName(PlatformType type) { diff --git a/elenasrc3/engine/linux/presenter.cpp b/elenasrc3/engine/linux/presenter.cpp index 3b75bb8bf..faaacabdd 100644 --- a/elenasrc3/engine/linux/presenter.cpp +++ b/elenasrc3/engine/linux/presenter.cpp @@ -3,7 +3,7 @@ // // This file contains the Windows Presenter implementation // -// (C)2021-2023, by Aleksey Rakov +// (C)2021-2024, by Aleksey Rakov //--------------------------------------------------------------------------- #include "presenter.h" @@ -104,6 +104,16 @@ void LinuxConsolePresenter :: printLine(ustr_t msg, ustr_t arg1, ustr_t arg2, us ::printLine(msg.str(), arg1.str(), arg2.str(), arg3.str()); } +void LinuxConsolePresenter :: printLine(ustr_t msg, int arg1, int arg2, int arg3, ustr_t arg4) +{ + ::printLine(msg.str(), arg1, arg2, arg3, arg4.str()); +} + +void LinuxConsolePresenter::printLine(ustr_t msg, ustr_t path, int col, int row, ustr_t s) +{ + ::printLine(msg.str(), path.str(), row, col, s.str()); +} + void LinuxConsolePresenter :: printLine(ustr_t msg, int arg1, int arg2, int arg3) { ::printLine(msg.str(), arg1, arg2, arg3); @@ -134,11 +144,6 @@ void LinuxConsolePresenter :: printLine(ustr_t msg) ::printLine(msg.str()); } -void LinuxConsolePresenter :: printLine(ustr_t msg, ustr_t path, int col, int row, ustr_t s) -{ - ::printLine(msg.str(), path.str(), row, col, s.str()); -} - void LinuxConsolePresenter::showProgress() { ::print("."); diff --git a/elenasrc3/engine/linux/presenter.h b/elenasrc3/engine/linux/presenter.h index 1deab3617..060c8e4b6 100644 --- a/elenasrc3/engine/linux/presenter.h +++ b/elenasrc3/engine/linux/presenter.h @@ -3,7 +3,7 @@ // // This file contains the Windows Presenter declaration // -// (C)2021-2023, by Aleksey Rakov +// (C)2021-2024, by Aleksey Rakov //--------------------------------------------------------------------------- #ifndef PRESENTER_H @@ -18,28 +18,30 @@ namespace elena_lang public: void readLine(char* buffer, size_t length) override; + void print(ustr_t msg) override; void print(ustr_t msg, ustr_t arg) override; void print(ustr_t msg, ustr_t arg1, ustr_t arg2) override; void print(ustr_t msg, ustr_t arg1, ustr_t arg2, ustr_t arg3) override; - void print(ustr_t msg, int arg1, int arg2, int arg3) override; - void printPath(ustr_t msg, path_t arg1, int arg2, int arg3, ustr_t arg4) override; void print(ustr_t msg, int arg1) override; void print(ustr_t msg, int arg1, int arg2) override; - void printPath(ustr_t msg, path_t arg) override; - void print(ustr_t msg) override; + void print(ustr_t msg, int arg1, int arg2, int arg3) override; void print(ustr_t msg, ustr_t path, int col, int row, ustr_t s) override; + void printPath(ustr_t msg, path_t arg) override; + void printPath(ustr_t msg, path_t arg1, int arg2, int arg3, ustr_t arg4) override; + void printLine(ustr_t msg) override; void printLine(ustr_t msg, ustr_t arg) override; void printLine(ustr_t msg, ustr_t arg1, ustr_t arg2) override; void printLine(ustr_t msg, ustr_t arg1, ustr_t arg2, ustr_t arg3) override; - void printLine(ustr_t msg, int arg1, int arg2, int arg3) override; - void printPathLine(ustr_t msg, path_t arg1, int arg2, int arg3, ustr_t arg4) override; void printLine(ustr_t msg, int arg1) override; void printLine(ustr_t msg, int arg1, int arg2) override; - void printPathLine(ustr_t msg, path_t arg) override; - void printLine(ustr_t msg) override; + void printLine(ustr_t msg, int arg1, int arg2, int arg3) override; + void printLine(ustr_t msg, int arg1, int arg2, int arg3, ustr_t arg4) override; //version support print void printLine(ustr_t msg, ustr_t path, int col, int row, ustr_t s) override; + void printPathLine(ustr_t msg, path_t arg) override; + void printPathLine(ustr_t msg, path_t arg1, int arg2, int arg3, ustr_t arg4) override; + void showProgress() override; void stopProgress() override; }; diff --git a/elenasrc3/engine/rtmanager.cpp b/elenasrc3/engine/rtmanager.cpp index cc075873c..f92d74d74 100644 --- a/elenasrc3/engine/rtmanager.cpp +++ b/elenasrc3/engine/rtmanager.cpp @@ -85,12 +85,9 @@ bool RTManager :: readAddressInfo(addr_t retAddress, LibraryLoaderBase& provider } if (found) { - bool isClass = true; // if symbol if (symbol[0] == '#') { symbol += 1; - - isClass = false; } auto moduleInfo = provider.getDebugModule({ symbol }, true); diff --git a/elenasrc3/engine/syntaxtree.h b/elenasrc3/engine/syntaxtree.h index 61a6f703a..dd28c23ef 100644 --- a/elenasrc3/engine/syntaxtree.h +++ b/elenasrc3/engine/syntaxtree.h @@ -214,7 +214,6 @@ namespace elena_lang Row = 0x000202, ExternalTree = 0x000301, - YieldContext = 0x000302, Idle = 0x000F01, }; diff --git a/elenasrc3/engine/windows/winevents.h b/elenasrc3/engine/windows/winevents.h new file mode 100644 index 000000000..2ceb2d07d --- /dev/null +++ b/elenasrc3/engine/windows/winevents.h @@ -0,0 +1,69 @@ +//--------------------------------------------------------------------------- +// E L E N A P r o j e c t: ELENA Engine +// +// This file contains the Win32 event template class +// (C)2021-2024, by Aleksey Rakov +//--------------------------------------------------------------------------- + +#ifndef WIN32EVENT_H +#define WIN32EVENT_H + +#include + +namespace elena_lang +{ + // --- EventManager --- + template class EventManager + { + HANDLE _events[EventCount]; + + public: + void init(T startEvent) + { + for (T i = 0; i < EventCount; i++) + _events[i] = CreateEvent(nullptr, TRUE, i == startEvent ? TRUE : FALSE, nullptr);; + } + + void setEvent(T event) + { + SetEvent(_events[event]); + } + + void resetEvent(T event) + { + ResetEvent(_events[event]); + } + + int waitForAnyEvent() + { + return WaitForMultipleObjects(EventCount, _events, FALSE, INFINITE); + } + + bool waitForEvent(T event, int timeout = INFINITE) + { + return (WaitForSingleObject(_events[event], timeout) == WAIT_OBJECT_0); + } + + void close() + { + for (T i = 0; i < EventCount; i++) { + if (_events[i]) { + CloseHandle(_events[i]); + _events[i] = nullptr; + } + } + } + + EventManager() + { + for (T i = 0; i < EventCount; i++) + _events[i] = nullptr; + } + virtual ~EventManager() + { + close(); + } + }; +} + +#endif \ No newline at end of file diff --git a/elenasrc3/ide/idecontroller.cpp b/elenasrc3/ide/idecontroller.cpp index feb8c0cf5..c49ad0472 100644 --- a/elenasrc3/ide/idecontroller.cpp +++ b/elenasrc3/ide/idecontroller.cpp @@ -248,6 +248,7 @@ bool ProjectController :: startDebugger(ProjectModel& model, DebugActionResult& if (!target.empty()) { PathString exePath(*model.projectPath, target); + PathUtil::makeCorrectExePath(exePath); // provide the whole command line including the executable path and name PathString commandLine(exePath); @@ -787,7 +788,6 @@ int ProjectController :: openSingleFileProject(ProjectModel& model, path_t singl IdentifierString tmp(*name); model.package.copy(*tmp); model.target.copy(*tmp); - model.target.append(".exe"); //model.profile diff --git a/elenasrc3/ide/ideversion.h b/elenasrc3/ide/ideversion.h index d6ac43e9e..5128be75a 100644 --- a/elenasrc3/ide/ideversion.h +++ b/elenasrc3/ide/ideversion.h @@ -1,2 +1,2 @@ -#define IDE_REVISION_NUMBER 12 +#define IDE_REVISION_NUMBER 14 diff --git a/elenasrc3/ide/vs/elide.vcxproj b/elenasrc3/ide/vs/elide.vcxproj index f591cbee1..f23c48619 100644 --- a/elenasrc3/ide/vs/elide.vcxproj +++ b/elenasrc3/ide/vs/elide.vcxproj @@ -182,6 +182,7 @@ + diff --git a/elenasrc3/ide/windows/win32debugprocess.cpp b/elenasrc3/ide/windows/win32debugprocess.cpp index 517996776..5fc202bc4 100644 --- a/elenasrc3/ide/windows/win32debugprocess.cpp +++ b/elenasrc3/ide/windows/win32debugprocess.cpp @@ -112,46 +112,6 @@ BOOL WINAPI debugEventThread(DebugControllerBase* controller) return TRUE; } -// --- DebugEventManager --- - -void DebugEventManager :: init() -{ - _events[DEBUG_ACTIVE] = CreateEvent(nullptr, TRUE, TRUE, nullptr); - _events[DEBUG_CLOSE] = CreateEvent(nullptr, TRUE, FALSE, nullptr); - _events[DEBUG_SUSPEND] = CreateEvent(nullptr, TRUE, FALSE, nullptr); - _events[DEBUG_RESUME] = CreateEvent(nullptr, TRUE, FALSE, nullptr); -} - -void DebugEventManager :: setEvent(int event) -{ - SetEvent(_events[event]); -} - -void DebugEventManager :: resetEvent(int event) -{ - ResetEvent(_events[event]); -} - -bool DebugEventManager :: waitForEvent(int event, int timeout) -{ - return (WaitForSingleObject(_events[event], timeout) == WAIT_OBJECT_0); -} - -int DebugEventManager :: waitForAnyEvent() -{ - return WaitForMultipleObjects(MAX_DEBUG_EVENT, _events, FALSE, INFINITE); -} - -void DebugEventManager :: close() -{ - for (int i = 0; i < MAX_DEBUG_EVENT; i++) { - if (_events[i]) { - CloseHandle(_events[i]); - _events[i] = nullptr; - } - } -} - // --- Win32ThreadContext --- Win32ThreadContext :: Win32ThreadContext(HANDLE hProcess, HANDLE hThread) @@ -358,7 +318,7 @@ void Win32DebugProcess::ConsoleHelper :: printText(const char* s) { HANDLE output_handle = GetStdHandle(STD_OUTPUT_HANDLE); - WriteConsoleA(output_handle, s, getlength(s), 0, 0); + WriteConsoleA(output_handle, s, getlength_pos(s), 0, 0); } void Win32DebugProcess::ConsoleHelper :: waitForAnyKey() @@ -486,7 +446,7 @@ void Win32DebugProcess :: processEnd() } } -void Win32DebugProcess :: processEvent(size_t timeout) +void Win32DebugProcess :: processEvent(DWORD timeout) { DEBUG_EVENT event; diff --git a/elenasrc3/ide/windows/win32debugprocess.h b/elenasrc3/ide/windows/win32debugprocess.h index 9ccb72000..72ed22815 100644 --- a/elenasrc3/ide/windows/win32debugprocess.h +++ b/elenasrc3/ide/windows/win32debugprocess.h @@ -9,33 +9,12 @@ #define WIN32DEBUGPROCESS_H #include "idecommon.h" -#include +#include "windows/winevents.h" namespace elena_lang { // --- DebugEventManager --- - class DebugEventManager - { - HANDLE _events[MAX_DEBUG_EVENT]; - - public: - void init(); - void setEvent(int event); - void resetEvent(int event); - int waitForAnyEvent(); - bool waitForEvent(int event, int timeout); - void close(); - - DebugEventManager() - { - for (int i = 0; i < MAX_DEBUG_EVENT; i++) - _events[i] = nullptr; - } - ~DebugEventManager() - { - close(); - } - }; + typedef EventManager DebugEventManager; class Win32DebugProcess; struct Win32BreakpointContext; @@ -159,7 +138,7 @@ namespace elena_lang bool startProcess(const wchar_t* exePath, const wchar_t* cmdLine, bool withPersistentConsole); void continueProcess(); - void processEvent(size_t timeout); + void processEvent(DWORD timeout); void processException(EXCEPTION_DEBUG_INFO* exception); void processStep(); void processEnd(); @@ -167,7 +146,7 @@ namespace elena_lang public: void initEvents() override { - _events.init(); + _events.init(DEBUG_ACTIVE); } void setEvent(int event) override { diff --git a/elenasrc3/ldbg/ldbg_common.h b/elenasrc3/ldbg/ldbg_common.h new file mode 100644 index 000000000..ed7a820be --- /dev/null +++ b/elenasrc3/ldbg/ldbg_common.h @@ -0,0 +1,28 @@ +//--------------------------------------------------------------------------- +// E L E N A P r o j e c t: ELENA command-Line Debugger Adapter +// +// This file contains LDBG comment declaration +// +// (C)2024, by Aleksey Rakov +//--------------------------------------------------------------------------- + +#ifndef LDBG_COMMON +#define LDBG_COMMON + +#if defined(_MSC_VER) + +#include "windows\winevents.h" + +#else + +#endif + +namespace elena_lang +{ + constexpr auto LDBG_EVENT_COUNT = 1; + constexpr auto LDBF_CONFIGURED = 0; + + typedef EventManager DPAEventManager; +} + +#endif // LDBG_COMMON diff --git a/elenasrc3/ldbg/ldbg_const.h b/elenasrc3/ldbg/ldbg_const.h index eb7fd79e5..43d78ac60 100644 --- a/elenasrc3/ldbg/ldbg_const.h +++ b/elenasrc3/ldbg/ldbg_const.h @@ -1 +1 @@ -#define LDBG_REVISION_NUMBER 0x0001 +#define LDBG_REVISION_NUMBER 0x0002 diff --git a/elenasrc3/ldbg/ldbg_session.cpp b/elenasrc3/ldbg/ldbg_session.cpp index 91ca8a3ba..e6877fe69 100644 --- a/elenasrc3/ldbg/ldbg_session.cpp +++ b/elenasrc3/ldbg/ldbg_session.cpp @@ -15,7 +15,7 @@ using namespace elena_lang; DPASessionWrapper :: DPASessionWrapper() { - + _events.init(-1); } DPASessionWrapper :: ~DPASessionWrapper() @@ -26,10 +26,26 @@ DPASessionWrapper :: ~DPASessionWrapper() void DPASessionWrapper :: prepare() { _session = new dpa::Session(); + + // The ConfigurationDone request is made by the client once all configuration + // requests have been made. + + //session->registerHandler([&](const dap::ConfigurationDoneRequest&) { + // configured.fire(); + // return dap::ConfigurationDoneResponse(); + // }); + +} + +void DPASessionWrapper :: bind() +{ + _session->connect(); } void DPASessionWrapper :: run() { - // Wait for the ConfigurationDone request to be made. + _session->start(); + // Wait for the ConfigurationDone request to be made. + _events.waitForEvent(LDBF_CONFIGURED); } diff --git a/elenasrc3/ldbg/ldbg_session.h b/elenasrc3/ldbg/ldbg_session.h index c4df827a7..8a8015d61 100644 --- a/elenasrc3/ldbg/ldbg_session.h +++ b/elenasrc3/ldbg/ldbg_session.h @@ -10,15 +10,19 @@ #define LDBG_SESSION_H #include "dpa_session.h" +#include "ldbg_common.h" namespace elena_lang { class DPASessionWrapper { + DPAEventManager _events; + dpa::Session* _session; public: void prepare(); + void bind(); void run(); DPASessionWrapper(); diff --git a/elenasrc3/ldbg/vs/ldbg.vcxproj b/elenasrc3/ldbg/vs/ldbg.vcxproj index 59c821d92..e953b5af0 100644 --- a/elenasrc3/ldbg/vs/ldbg.vcxproj +++ b/elenasrc3/ldbg/vs/ldbg.vcxproj @@ -101,6 +101,7 @@ WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true ..\..\common;..\..\engine;..\..\dpa;..\windows;..;%(AdditionalIncludeDirectories) + stdcpp17 Console @@ -119,6 +120,7 @@ WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true ..\..\common;..\..\engine;..\..\dpa;..\windows;..;%(AdditionalIncludeDirectories) + stdcpp17 Console @@ -137,6 +139,7 @@ _DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true ..\..\common;..\..\engine;..\..\dpa;..\windows;..;%(AdditionalIncludeDirectories) + stdcpp17 Console @@ -155,6 +158,7 @@ NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true ..\..\common;..\..\engine;..\..\dpa;..\windows;..;%(AdditionalIncludeDirectories) + stdcpp17 Console @@ -167,14 +171,17 @@ + + + + - diff --git a/elenasrc3/ldbg/windows/ldbg.cpp b/elenasrc3/ldbg/windows/ldbg.cpp index 65b102626..417e692c0 100644 --- a/elenasrc3/ldbg/windows/ldbg.cpp +++ b/elenasrc3/ldbg/windows/ldbg.cpp @@ -25,6 +25,7 @@ int main() DPASessionWrapper session; session.prepare(); + session.bind(); session.run(); return 0; diff --git a/elenasrc3/tools/ecv/ecvconst.h b/elenasrc3/tools/ecv/ecvconst.h index e3a8503fd..6e9c7eff3 100644 --- a/elenasrc3/tools/ecv/ecvconst.h +++ b/elenasrc3/tools/ecv/ecvconst.h @@ -11,7 +11,7 @@ namespace elena_lang { - #define ECV_REVISION_NUMBER 0x0003 + #define ECV_REVISION_NUMBER 0x0004 constexpr auto ECV_GREETING = "ELENA command line ByteCode Viewer %d.%d.%d (C)2021-24 by Aleksey Rakov\n"; diff --git a/elenasrc3/tools/ecv/linux/ecv.cpp b/elenasrc3/tools/ecv/linux/ecv.cpp index 5bfefe70b..8bb46ded0 100644 --- a/elenasrc3/tools/ecv/linux/ecv.cpp +++ b/elenasrc3/tools/ecv/linux/ecv.cpp @@ -14,9 +14,6 @@ using namespace elena_lang; constexpr auto DEFAULT_CONFIG = "/etc/elena/templates/lib60.config"; -constexpr auto PLATFORM_CATEGORY = "configuration/platform"; -constexpr auto LIB_PATH = "project/libpath"; - #if defined(__x86_64__) constexpr auto PLATFORM_KEY = "Linux_AMD64"; @@ -92,7 +89,7 @@ class ConsoleHelper : public ConsoleHelperBase int main(int argc, char* argv[]) { - printf("ELENA command line ByteCode Viewer %d.%d.%d (C)2011-2022 by Aleksey Rakov\n", ENGINE_MAJOR_VERSION, ENGINE_MINOR_VERSION, ECV_REVISION_NUMBER); + printf(ECV_GREETING, ENGINE_MAJOR_VERSION, ENGINE_MINOR_VERSION, ECV_REVISION_NUMBER); // prepare library provider LibraryProvider provider; diff --git a/examples60/console/matrix/matrix.prj b/examples60/console/matrix/matrix.prj index 90f7d1cf9..17fef952c 100644 --- a/examples60/console/matrix/matrix.prj +++ b/examples60/console/matrix/matrix.prj @@ -1,25 +1,26 @@ - - - matrix - - - - - matrix64 - - - - matrix - - - - - matrix.l - - - - extensions'programLoop - matrix'control - + + + matrix + + + + + matrix64 + + + + + matrix + + + + + matrix.l + + + + extensions'programLoop + matrix'control + \ No newline at end of file diff --git a/src60/system/attributes/attributes.l b/src60/system/attributes/attributes.l index 47aea8bcb..213dc31c6 100644 --- a/src60/system/attributes/attributes.l +++ b/src60/system/attributes/attributes.l @@ -6,6 +6,7 @@ /// scope_accessors: #let attributes["get"] := 80005001h; #let attributes["set"] := 80005002h; +#let attributes["yield"] := 80005003h; /// visibility: #let attributes["public"] := 80004001h; @@ -17,7 +18,6 @@ #let attributes["sealed"] := 80003001h; #let attributes["abstract"] := 80003002h; #let attributes["closed"] := 80003003h; -#let attributes["yieldable"] := 80003004h; #let attributes["predefined"] := 80003005h; #let attributes["override"] := 80003006h; diff --git a/src60/system/inline_templates.l b/src60/system/inline_templates.l index ad9ad18e1..f8ca3de42 100644 --- a/src60/system/inline_templates.l +++ b/src60/system/inline_templates.l @@ -190,4 +190,24 @@ public enumeration enum ^ false } -} \ No newline at end of file +} + +// --- yield state machine --- + +public abstract class YieldStateEnumerator : Enumerator +{ + private __context; // context must be the first one + private T __current; + + get T Value() + = __current; + + reset() + { + NotSupportedException.raise() + } + + Enumerator cast() = new Enumerator { embeddable dispatch() => self; }; + + enumerable() = self; +} diff --git a/tests60/sandbox/sandbox.l b/tests60/sandbox/sandbox.l index 21f279e08..b0e0cff04 100644 --- a/tests60/sandbox/sandbox.l +++ b/tests60/sandbox/sandbox.l @@ -1,81 +1,33 @@ import extensions; -sealed struct OctalNumber +singleton IteratorEnumerable : Enumerable { - int value; - - int cast() = value; - - constructor(int n) - { - value := n; - } - - cast o(string s) - { - value := s.toInt(8); - } - - internal constructor sum(OctalNumber o1, OctalNumber o2) - { - int n1 := o1.Value; - int n2 := o2.Value; - - value := n1 + n2 - } - - internal constructor diff(OctalNumber o1, OctalNumber o2) - { - int n1 := o1.Value; - int n2 := o2.Value; - - value := n1 - n2 - } - - internal constructor prod(OctalNumber o1, OctalNumber o2) - { - int n1 := o1.Value; - int n2 := o2.Value; - - value := n1 * n2 - } - - internal constructor frac(OctalNumber o1, OctalNumber o2) - { - int n1 := o1.Value; - int n2 := o2.Value; - - value := n1 / n2 - } - - int Value = value; - - string toPrintable() - = value.toString(8); - - OctalNumber add(OctalNumber n) - = OctalNumber.sum(self, n); - - OctalNumber subtract(OctalNumber n) - = OctalNumber.diff(self, n); - - OctalNumber multiply(OctalNumber n) - = OctalNumber.prod(self, n); + Enumerator enumerator() + = IteratorEnumerable.infinitEnumerator(); + + yield Enumerator infinitEnumerator() + { + $yield 2; + $yield 5; + $yield 7; + } +} - OctalNumber divide(OctalNumber n) - = OctalNumber.frac(self, n); +public iteratorMethodTest() +{ + auto e := IteratorEnumerable.enumerator(); + + Assert.ifTrue(e.next()); + Assert.ifTrue(*e == 2); + Assert.ifTrue(e.next()); + Assert.ifTrue(*e == 5); + Assert.ifTrue(e.next()); + Assert.ifTrue(*e == 7); + Assert.ifFalse(e.next()); } + public program() { - var n := 7o; - var m := 2o; - console.printLine(n,"+",m,"=",n + m); - - n := 24o; - m := 7o; - console.printLine(n,"+",m,"=",n + m); - console.printLine(n,"-",m,"=",n - m); - console.printLine(n,"*",m,"=",n * m); - console.printLine(n,"/",m,"=",n / m); + iteratorMethodTest(); } diff --git a/tests60/system_tests/basic.l b/tests60/system_tests/basic.l index cdb70f475..511b58284 100644 --- a/tests60/system_tests/basic.l +++ b/tests60/system_tests/basic.l @@ -2076,3 +2076,32 @@ public stringInterpolation() : testCase() Assert.ifEqual(s,"a_1_b_2_c"); console.write("."); } + +// --- iteratorMethodTest --- + +singleton IteratorEnumerable : Enumerable +{ + Enumerator enumerator() + = IteratorEnumerable.infinitEnumerator(); + + yield Enumerator infinitEnumerator() + { + $yield 2; + $yield 5; + $yield 7; + } +} + +public iteratorMethodTest() : testCase() +{ + auto e := IteratorEnumerable.enumerator(); + + Assert.ifTrue(e.next()); + Assert.ifTrue(*e == 2); + Assert.ifTrue(e.next()); + Assert.ifTrue(*e == 5); + Assert.ifTrue(e.next()); + Assert.ifTrue(*e == 7); + Assert.ifFalse(e.next()); + console.write("."); +}