diff --git a/src/NUnitTests/CompilerTests.cs b/src/NUnitTests/CompilerTests.cs index 08b904ea9..4af1cdc21 100644 --- a/src/NUnitTests/CompilerTests.cs +++ b/src/NUnitTests/CompilerTests.cs @@ -35,7 +35,7 @@ public void TestNoSemicolonBeforeEndProcedure() Возврат КонецПроцедуры"); - _ = host.Engine.GetCompilerService().Compile(moduleSource); + host.Engine.GetCompilerService().Compile(moduleSource); } [Test] @@ -46,7 +46,7 @@ public void TestNoSemicolonBeforeEndFunction() Возврат 4 КонецФункции"); - _ = host.Engine.GetCompilerService().Compile(moduleSource); + host.Engine.GetCompilerService().Compile(moduleSource); } [Test] @@ -57,7 +57,7 @@ public void TestNoSemicolonBeforeEndDo() Прервать КонецЦикла"); - _ = host.Engine.GetCompilerService().Compile(moduleSource); + host.Engine.GetCompilerService().Compile(moduleSource); } [Test] @@ -72,7 +72,7 @@ ИначеЕсли Истина Тогда Ф = 3 КонецЕсли"); - _ = host.Engine.GetCompilerService().Compile(moduleSource); + host.Engine.GetCompilerService().Compile(moduleSource); } [Test] @@ -85,7 +85,7 @@ public void TestNoSemicolonBeforeExceptionOrEndTry() ВызватьИсключение КонецПопытки"); - _ = host.Engine.GetCompilerService().Compile(moduleSource); + host.Engine.GetCompilerService().Compile(moduleSource); } @@ -98,7 +98,7 @@ public void TestNoSemicolonBeforeEndOfText() КонецПроцедуры Ф = 0"); - _ = host.Engine.GetCompilerService().Compile(moduleSource); + host.Engine.GetCompilerService().Compile(moduleSource); } #endregion @@ -112,7 +112,7 @@ public void TestSemicolonBeforeEndProcedure() Возврат; КонецПроцедуры"); - _ = host.Engine.GetCompilerService().Compile(moduleSource); + host.Engine.GetCompilerService().Compile(moduleSource); } [Test] @@ -123,7 +123,7 @@ public void TestSemicolonBeforeEndFunction() Возврат 4; КонецФункции"); - _ = host.Engine.GetCompilerService().Compile(moduleSource); + host.Engine.GetCompilerService().Compile(moduleSource); } [Test] @@ -134,7 +134,7 @@ public void TestSemicolonBeforeEndDo() Прервать; КонецЦикла"); - _ = host.Engine.GetCompilerService().Compile(moduleSource); + host.Engine.GetCompilerService().Compile(moduleSource); } [Test] @@ -149,7 +149,7 @@ ИначеЕсли Истина Тогда Ф = 3; КонецЕсли"); - _ = host.Engine.GetCompilerService().Compile(moduleSource); + host.Engine.GetCompilerService().Compile(moduleSource); } [Test] @@ -162,7 +162,7 @@ public void TestSemicolonBeforeExceptionOrEndTry() ВызватьИсключение; КонецПопытки"); - _ = host.Engine.GetCompilerService().Compile(moduleSource); + host.Engine.GetCompilerService().Compile(moduleSource); } @@ -175,7 +175,7 @@ public void TestSemicolonBeforeEndOfText() КонецПроцедуры Ф = 0;"); - _ = host.Engine.GetCompilerService().Compile(moduleSource); + host.Engine.GetCompilerService().Compile(moduleSource); } #endregion @@ -192,7 +192,7 @@ public void TestEndFunctionDoesNotEndIf() bool exceptionThrown = false; try { - _ = host.Engine.GetCompilerService().Compile(moduleSource); + host.Engine.GetCompilerService().Compile(moduleSource); } catch (CompilerException) { @@ -212,7 +212,7 @@ public void TestEndDoDoesNotEndIf() bool exceptionThrown = false; try { - _ = host.Engine.GetCompilerService().Compile(moduleSource); + host.Engine.GetCompilerService().Compile(moduleSource); } catch (CompilerException) { @@ -232,7 +232,7 @@ public void TestEndIfDoesNotEndDo() bool exceptionThrown = false; try { - _ = host.Engine.GetCompilerService().Compile(moduleSource); + host.Engine.GetCompilerService().Compile(moduleSource); } catch (CompilerException) { @@ -251,7 +251,7 @@ public void TestEndFunctionDoesNotEndProcedure() bool exceptionThrown = false; try { - _ = host.Engine.GetCompilerService().Compile(moduleSource); + host.Engine.GetCompilerService().Compile(moduleSource); } catch (CompilerException) { @@ -271,7 +271,7 @@ Возврат 0 bool exceptionThrown = false; try { - _ = host.Engine.GetCompilerService().Compile(moduleSource); + host.Engine.GetCompilerService().Compile(moduleSource); } catch (CompilerException) { @@ -291,7 +291,7 @@ public void TestElseifDoesNotEndProcedure() bool exceptionThrown = false; try { - _ = host.Engine.GetCompilerService().Compile(moduleSource); + host.Engine.GetCompilerService().Compile(moduleSource); } catch (CompilerException) { @@ -311,7 +311,7 @@ public void TestEndTryDoesNotEndProcedure() bool exceptionThrown = false; try { - _ = host.Engine.GetCompilerService().Compile(moduleSource); + host.Engine.GetCompilerService().Compile(moduleSource); } catch (CompilerException) { @@ -339,7 +339,7 @@ Функция Ф1(П1, П2 = Неопределено, П3 = Неопредел Р = Ф1(,,) + Ф1(,); "); - _ = host.Engine.GetCompilerService().Compile(moduleSource); + host.Engine.GetCompilerService().Compile(moduleSource); } [Test(Description = "Не компилируется вызов метода вообще без параметров")] @@ -355,7 +355,7 @@ public void TestCantCompileCallWithoutParams() bool exceptionThrown = false; try { - _ = host.Engine.GetCompilerService().Compile(moduleSource); + host.Engine.GetCompilerService().Compile(moduleSource); } catch (CompilerException) { @@ -374,7 +374,7 @@ public void TestReturnBeforeException() Исключение КонецПопытки КонецПроцедуры"); - _ = host.Engine.GetCompilerService().Compile(moduleSource); + host.Engine.GetCompilerService().Compile(moduleSource); } @@ -389,7 +389,7 @@ ДобавитьОбработчик ЭтотОбъект.Событие Ина bool exceptionThrown = false; try { - _ = host.Engine.GetCompilerService().Compile(moduleSource); + host.Engine.GetCompilerService().Compile(moduleSource); } catch (CompilerException) { @@ -411,7 +411,7 @@ Процедура Проц1() bool exceptionThrown = false; try { - _ = host.Engine.GetCompilerService().Compile(moduleSource); + host.Engine.GetCompilerService().Compile(moduleSource); } catch (CompilerException) { @@ -434,7 +434,7 @@ Если Истина Тогда bool exceptionThrown = false; try { - _ = host.Engine.GetCompilerService().Compile(moduleSource); + host.Engine.GetCompilerService().Compile(moduleSource); } catch (CompilerException) { @@ -457,7 +457,7 @@ Процедура Проц2() bool exceptionThrown = false; try { - _ = host.Engine.GetCompilerService().Compile(moduleSource); + host.Engine.GetCompilerService().Compile(moduleSource); } catch (CompilerException) { @@ -479,7 +479,7 @@ Если Ложь Тогда bool exceptionThrown = false; try { - _ = host.Engine.GetCompilerService().Compile(moduleSource); + host.Engine.GetCompilerService().Compile(moduleSource); } catch (CompilerException) { @@ -501,7 +501,7 @@ Если Ложь Тогда bool exceptionThrown = false; try { - _ = host.Engine.GetCompilerService().Compile(moduleSource); + host.Engine.GetCompilerService().Compile(moduleSource); } catch (CompilerException) { @@ -524,7 +524,7 @@ Если Ложь Тогда bool exceptionThrown = false; try { - _ = host.Engine.GetCompilerService().Compile(moduleSource); + host.Engine.GetCompilerService().Compile(moduleSource); } catch (CompilerException) { @@ -532,5 +532,25 @@ Если Ложь Тогда } Assert.IsTrue(exceptionThrown, "Отсутствует точка с запятой между операторами!"); } - } + + [Test] + public void TestLocalExportVar() + { + var moduleSource = host.Engine.Loader.FromString( + @"Процедура Проц1() + Перем Переменная Экспорт; + КонецПроцедуры"); + + bool exceptionThrown = false; + try + { + host.Engine.GetCompilerService().Compile(moduleSource); + } + catch (CompilerException) + { + exceptionThrown = true; + } + Assert.IsTrue(exceptionThrown, "В теле процедуры или функции не может быть объявлена экспортная переменная!"); + } + } } \ No newline at end of file diff --git a/src/ScriptEngine.HostedScript/Library/Reflector.cs b/src/ScriptEngine.HostedScript/Library/Reflector.cs index ef00e1d06..c3cc70776 100644 --- a/src/ScriptEngine.HostedScript/Library/Reflector.cs +++ b/src/ScriptEngine.HostedScript/Library/Reflector.cs @@ -274,7 +274,7 @@ private static AnnotationDefinition[] GetAnnotations(IEnumerable x.Annotation).ToArray(); } - private static void FillPropertiesTableForType(TypeTypeValue type, ValueTable.ValueTable result) + private static void FillPropertiesTableForType(TypeTypeValue type, ValueTable.ValueTable result, bool withPrivate = false) { var clrType = GetReflectableClrType(type); var nativeProps = clrType.GetProperties() @@ -299,14 +299,19 @@ private static void FillPropertiesTableForType(TypeTypeValue type, ValueTable.Va if (clrType.BaseType == typeof(ScriptDrivenObject)) { - var nativeFields = clrType.GetFields(); - foreach(var field in nativeFields) + var flags = BindingFlags.Public; + if (withPrivate) + flags |= BindingFlags.NonPublic; + + var nativeFields = clrType.GetFields(flags); + foreach (var field in nativeFields) { var info = new VariableInfo(); info.Type = SymbolType.ContextProperty; info.Index = indices++; info.Identifier = field.Name; info.Annotations = GetAnnotations(field.GetCustomAttributes()); + info.IsExport = field.IsPublic; infos.Add(info); } } @@ -365,18 +370,19 @@ private static void FillMethodsTable(ValueTable.ValueTable result, IEnumerable /// Объект, из которого получаем таблицу свойств. + /// Включая приватные /// Таблица значений с колонками - Имя, Аннотации [ContextMethod("ПолучитьТаблицуСвойств", "GetPropertiesTable")] - public ValueTable.ValueTable GetPropertiesTable(IValue target) + public ValueTable.ValueTable GetPropertiesTable(IValue target, bool withPrivate = false) { ValueTable.ValueTable result = new ValueTable.ValueTable(); if(target.DataType == DataType.Object) - FillPropertiesTable(result, target.AsObject().GetProperties()); + FillPropertiesTable(result, target.AsObject().GetProperties(withPrivate)); else if (target.DataType == DataType.Type) { var type = target.GetRawValue() as TypeTypeValue; - FillPropertiesTableForType(type, result); + FillPropertiesTableForType(type, result, withPrivate); } else throw RuntimeException.InvalidArgumentType(); @@ -422,6 +428,7 @@ private static void FillPropertiesTable(ValueTable.ValueTable result, IEnumerabl { var nameColumn = result.Columns.Add("Имя", TypeDescription.StringType(), "Имя"); var annotationsColumn = result.Columns.Add("Аннотации", new TypeDescription(), "Аннотации"); + var exportColumn = result.Columns.Add("Экспорт", TypeDescription.BooleanType(), "Экспорт"); var systemVarNames = new string[] { "этотобъект", "thisobject" }; foreach (var propInfo in properties) @@ -432,6 +439,7 @@ private static void FillPropertiesTable(ValueTable.ValueTable result, IEnumerabl new_row.Set(nameColumn, ValueFactory.Create(propInfo.Identifier)); new_row.Set(annotationsColumn, propInfo.AnnotationsCount != 0 ? CreateAnnotationTable(propInfo.Annotations) : EmptyAnnotationsTable()); + new_row.Set(exportColumn, ValueFactory.Create(propInfo.IsExport)); } } diff --git a/src/ScriptEngine/Compiler/Compiler.cs b/src/ScriptEngine/Compiler/Compiler.cs index 4b6aad259..69adf0b22 100644 --- a/src/ScriptEngine/Compiler/Compiler.cs +++ b/src/ScriptEngine/Compiler/Compiler.cs @@ -325,12 +325,18 @@ private void BuildVariableDefinitions() var symbolicName = _lastExtractedLexem.Content; var annotations = ExtractAnnotations(); var definition = _ctx.DefineVariable(symbolicName); + NextToken(); if (_inMethodScope) { if (_isStatementsDefined) { throw CompilerException.LateVarDefinition(); } + + if(_lastExtractedLexem.Token == Token.Export) + { + throw CompilerException.LocalExportVar(); + } } else { @@ -346,10 +352,10 @@ private void BuildVariableDefinitions() Annotations = annotations, CanGet = true, CanSet = true, - Index = definition.CodeIndex + Index = definition.CodeIndex, + IsExport = _lastExtractedLexem.Token == Token.Export }); } - NextToken(); if (_lastExtractedLexem.Token == Token.Export) { _module.ExportedProperties.Add(new ExportedSymbol() diff --git a/src/ScriptEngine/Compiler/CompilerExceptions.cs b/src/ScriptEngine/Compiler/CompilerExceptions.cs index 0f38e1677..fa7d3977e 100644 --- a/src/ScriptEngine/Compiler/CompilerExceptions.cs +++ b/src/ScriptEngine/Compiler/CompilerExceptions.cs @@ -51,6 +51,12 @@ internal static CompilerException LateVarDefinition() + "en='Variable declarations must be placed at beginning of module, procedure, or function'")); } + internal static CompilerException LocalExportVar() + { + return new CompilerException(Locale.NStr("ru='В теле процедуры или функции не может быть объявлена экспортная переменная';" + + "en='An export variable cannot be declared in the body of a procedure or function'")); + } + internal static CompilerException TokenExpected(params Token[] expected) { var names = expected.Select(x => Enum.GetName(typeof(Token), x)); diff --git a/src/ScriptEngine/Machine/Core.cs b/src/ScriptEngine/Machine/Core.cs index 321e65215..b3ee5edef 100644 --- a/src/ScriptEngine/Machine/Core.cs +++ b/src/ScriptEngine/Machine/Core.cs @@ -311,6 +311,7 @@ public struct VariableInfo public string Identifier; public string Alias; public SymbolType Type; + public bool IsExport; public bool CanGet; public bool CanSet; diff --git a/src/ScriptEngine/Machine/IRuntimeContextInstance.cs b/src/ScriptEngine/Machine/IRuntimeContextInstance.cs index a14ac769c..c9e7f9ab3 100644 --- a/src/ScriptEngine/Machine/IRuntimeContextInstance.cs +++ b/src/ScriptEngine/Machine/IRuntimeContextInstance.cs @@ -50,7 +50,32 @@ public static IEnumerable GetMethods(this IRuntimeContextInstance co return methods; } - public static IEnumerable GetProperties(this IRuntimeContextInstance context) + public static IEnumerable GetProperties(this IRuntimeContextInstance context, bool withPrivate = false) + { + return withPrivate ? GetPropertiesWithPrivate(context) : GetPropertiesWithoutPrivate(context); + } + + private static IEnumerable GetPropertiesWithPrivate(IRuntimeContextInstance context) + { + if (!(context is UserScriptContextInstance userScript)) + return Array.Empty(); + + List infos = new List(); + foreach (var variable in userScript.Module.Variables) + { + infos.Add(new VariableInfo() { + Identifier = variable.Identifier, + Type = variable.Type, + Index = variable.Index, + Annotations = HackGetAnnotations(context, variable.Index), + IsExport = variable.IsExport + }); + } + + return infos; + } + + private static IEnumerable GetPropertiesWithoutPrivate(IRuntimeContextInstance context) { VariableInfo[] infos = new VariableInfo[context.GetPropCount()]; for (int i = 0; i < infos.Length; i++) @@ -60,7 +85,8 @@ public static IEnumerable GetProperties(this IRuntimeContextInstan Identifier = context.GetPropName(i), Type = SymbolType.ContextProperty, Index = i, - Annotations = HackGetAnnotations(context, i) + Annotations = HackGetAnnotations(context, i), + IsExport = true }; } diff --git a/tests/reflector.os b/tests/reflector.os index d5a876886..9f69f5986 100644 --- a/tests/reflector.os +++ b/tests/reflector.os @@ -37,6 +37,7 @@ ВсеТесты.Добавить("ТестДолжен_ПроверитьМетод_ПолучитьТаблицуМетодов_ПроверитьЗначенияПоУмолчанию"); ВсеТесты.Добавить("ТестДолжен_ПроверитьМетод_ПолучитьТаблицуМетодов_ПроверитьЗначенияПоУмолчанию_ИзТипаОбъекта"); ВсеТесты.Добавить("ТестДолжен_ПроверитьПустыеАннотации"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПриватныеПоля"); Возврат ВсеТесты; КонецФункции @@ -553,4 +554,69 @@ юТест.ПроверитьРавенство(Яшма2.Аннотации[0].Параметры[0].Значение, 1); юТест.ПроверитьРавенство(ПеременнаяСАннотациейИПараметром.Аннотации[0].Параметры[0].Значение, 1); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПриватныеПоля() Экспорт + Пример = ПолучитьОбъектДляПроверки("test_reflector"); + + Рефлектор = Новый Рефлектор; + ТаблицаСвойств = Рефлектор.ПолучитьТаблицуСвойств(Пример, Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств.Количество(), 7); + юТест.ПроверитьРавенство(ТаблицаСвойств[0].Имя, "ПеременнаяСАннотацией"); + юТест.ПроверитьРавенство(ТаблицаСвойств[0].Экспорт, Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств[1].Имя, "ПеременнаяСАннотациейИПараметром"); + юТест.ПроверитьРавенство(ТаблицаСвойств[1].Экспорт, Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств[2].Имя, "юТест"); + юТест.ПроверитьРавенство(ТаблицаСвойств[2].Экспорт, Ложь); + юТест.ПроверитьРавенство(ТаблицаСвойств[3].Имя, "ЭтотОбъек"); + юТест.ПроверитьРавенство(ТаблицаСвойств[3].Экспорт, Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств[4].Имя, "ЭкспортнаяПеременная"); + юТест.ПроверитьРавенство(ТаблицаСвойств[4].Экспорт, Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств[5].Имя, "Яшма1"); + юТест.ПроверитьРавенство(ТаблицаСвойств[5].Экспорт, Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств[6].Имя, "Яшма2"); + юТест.ПроверитьРавенство(ТаблицаСвойств[6].Экспорт, Ложь); + + ТаблицаСвойств = Рефлектор.ПолучитьТаблицуСвойств(Пример); + юТест.ПроверитьРавенство(ТаблицаСвойств.Количество(), 5); + юТест.ПроверитьРавенство(ТаблицаСвойств[0].Имя, "ПеременнаяСАннотацией"); + юТест.ПроверитьРавенство(ТаблицаСвойств[0].Экспорт, Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств[1].Имя, "ПеременнаяСАннотациейИПараметром"); + юТест.ПроверитьРавенство(ТаблицаСвойств[1].Экспорт, Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств[2].Имя, "ЭтотОбъек"); + юТест.ПроверитьРавенство(ТаблицаСвойств[2].Экспорт, Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств[3].Имя, "ЭкспортнаяПеременная"); + юТест.ПроверитьРавенство(ТаблицаСвойств[3].Экспорт, Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств[4].Имя, "Яшма1"); + юТест.ПроверитьРавенство(ТаблицаСвойств[4].Экспорт, Истина); + + ТаблицаСвойств = Рефлектор.ПолучитьТаблицуСвойств(ТипЗнч(Пример), Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств.Количество(), 7); + юТест.ПроверитьРавенство(ТаблицаСвойств[0].Имя, "ПеременнаяСАннотацией"); + юТест.ПроверитьРавенство(ТаблицаСвойств[0].Экспорт, Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств[1].Имя, "ПеременнаяСАннотациейИПараметром"); + юТест.ПроверитьРавенство(ТаблицаСвойств[1].Экспорт, Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств[2].Имя, "юТест"); + юТест.ПроверитьРавенство(ТаблицаСвойств[2].Экспорт, Ложь); + юТест.ПроверитьРавенство(ТаблицаСвойств[3].Имя, "ЭтотОбъек"); + юТест.ПроверитьРавенство(ТаблицаСвойств[3].Экспорт, Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств[4].Имя, "ЭкспортнаяПеременная"); + юТест.ПроверитьРавенство(ТаблицаСвойств[4].Экспорт, Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств[5].Имя, "Яшма1"); + юТест.ПроверитьРавенство(ТаблицаСвойств[5].Экспорт, Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств[6].Имя, "Яшма2"); + юТест.ПроверитьРавенство(ТаблицаСвойств[6].Экспорт, Ложь); + + ТаблицаСвойств = Рефлектор.ПолучитьТаблицуСвойств(ТипЗнч(Пример)); + юТест.ПроверитьРавенство(ТаблицаСвойств.Количество(), 5); + юТест.ПроверитьРавенство(ТаблицаСвойств[0].Имя, "ПеременнаяСАннотацией"); + юТест.ПроверитьРавенство(ТаблицаСвойств[0].Экспорт, Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств[1].Имя, "ПеременнаяСАннотациейИПараметром"); + юТест.ПроверитьРавенство(ТаблицаСвойств[1].Экспорт, Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств[2].Имя, "ЭтотОбъек"); + юТест.ПроверитьРавенство(ТаблицаСвойств[2].Экспорт, Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств[3].Имя, "ЭкспортнаяПеременная"); + юТест.ПроверитьРавенство(ТаблицаСвойств[3].Экспорт, Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств[4].Имя, "Яшма1"); + юТест.ПроверитьРавенство(ТаблицаСвойств[4].Экспорт, Истина); КонецПроцедуры \ No newline at end of file