Example #1
0
        public static DependencyEmbedderAspect Read(AttributeData attributeData, IDiagnosticSink diagnosticSink)
        {
            DependencyEmbedderAspect config = new DependencyEmbedderAspect();
            var namedArguments = attributeData.NamedArguments;

            config.DisableCleanup            = namedArguments.GetSafeBool(nameof(DependencyEmbedderAspect.DisableCleanup), false);
            config.DisableCompression        = namedArguments.GetSafeBool(nameof(DependencyEmbedderAspect.DisableCompression), false);
            config.IncludeDebugSymbols       = namedArguments.GetSafeBool(nameof(DependencyEmbedderAspect.IncludeDebugSymbols), true);
            config.CreateTemporaryAssemblies = namedArguments.GetSafeBool(nameof(DependencyEmbedderAspect.CreateTemporaryAssemblies), false);
            config.IgnoreSatelliteAssemblies = namedArguments.GetSafeBool(nameof(DependencyEmbedderAspect.IgnoreSatelliteAssemblies), false);

            config.IncludeAssemblies     = namedArguments.GetSafeStringArray(nameof(DependencyEmbedderAspect.IncludeAssemblies));
            config.ExcludeAssemblies     = namedArguments.GetSafeStringArray(nameof(DependencyEmbedderAspect.ExcludeAssemblies));
            config.PreloadOrder          = namedArguments.GetSafeStringArray(nameof(DependencyEmbedderAspect.PreloadOrder));
            config.Unmanaged32Assemblies = namedArguments.GetSafeStringArray(nameof(DependencyEmbedderAspect.Unmanaged32Assemblies));
            config.Unmanaged64Assemblies = namedArguments.GetSafeStringArray(nameof(DependencyEmbedderAspect.Unmanaged64Assemblies));

            if (config.IncludeAssemblies != null && config.IncludeAssemblies.Length > 0 &&
                config.ExcludeAssemblies != null && config.ExcludeAssemblies.Length > 0)
            {
                var syntaxReference = attributeData.ApplicationSyntaxReference;
                diagnosticSink.AddDiagnostic(Diagnostic.Create(
                                                 "DE002", "Caravela.Open.DependencyEmbedder", "Set IncludeAssemblies, or ExcludeAssemblies, but not both.", DiagnosticSeverity.Error, DiagnosticSeverity.Error, true, 0,
                                                 location: Location.Create(syntaxReference.SyntaxTree, syntaxReference.Span)));
            }
            return(config);
        }
Example #2
0
        /// <summary>
        /// Tries to resolve the given type name.
        /// </summary>
        /// <param name="type">The simple or full type name.</param>
        /// <param name="diagnostics">A diagnostics sink for resolution errors.</param>
        /// <param name="resolvedType">If this method returns true, the resolved type.</param>
        public static bool TryResolve(TypeSyntax type, IDiagnosticSink diagnostics,
                                      [NotNullWhen(true)] out TypeDefinition?resolvedType)
        {
            // TODO: Proper type resolution with a declaration provider
            switch (((TypeNameSyntax)type).TypeName)
            {
            case "bool":
                resolvedType = SimpleType.Bool;
                break;

            case "int32":
                resolvedType = SimpleType.Int32;
                break;

            case "void":
                resolvedType = SimpleType.Void;
                break;

            default:
                diagnostics.Add(DiagnosticCode.TypeNotFound, type.Position, type.ToString());
                resolvedType = null;
                return(false);
            }

            return(true);
        }
Example #3
0
 /// <summary>
 /// Creates an instance that can be used for method body compilation.
 /// The instance can be reused for successive <see cref="CompileBody"/> calls.
 /// </summary>
 /// <param name="declarationProvider">The provider for method and type declarations.</param>
 /// <param name="diagnosticSink">The receiver for any semantic errors or warnings.</param>
 public MethodCompiler(
     IDeclarationProvider declarationProvider,
     IDiagnosticSink diagnosticSink)
 {
     _declarationProvider = declarationProvider;
     _diagnostics         = diagnosticSink;
     _variableMap         = new ScopedVariableMap();
 }
Example #4
0
        /// <summary>
        /// Tries to parse the given source file.
        /// If the file cannot be parsed correctly, returns null.
        /// </summary>
        /// <param name="source">The UTF-8 encoded source file content.</param>
        /// <param name="filename">The name of the source file.</param>
        /// <param name="diagnosticSink">The sink to write parse diagnostics into.</param>
        public static SourceFileSyntax?Parse(
            Memory <byte> source,
            string filename,
            IDiagnosticSink diagnosticSink)
        {
            var parser = new SyntaxParser(source, filename, diagnosticSink);

            return(parser.ParseSourceFile());
        }
Example #5
0
        private static bool TryCompileCall(FunctionCallSyntax call, CompiledMethod method, BasicBlockBuilder builder,
                                           IDiagnosticSink diagnostics, INameResolver nameResolver, out Temporary value)
        {
            value = default;

            // Get the callee
            var matchingMethods = nameResolver.ResolveMethod(call.Function.Name);

            if (matchingMethods.Count == 0)
            {
                diagnostics.Add(DiagnosticCode.MethodNotFound, call.Function.Position, call.Function.Name);
                return(false);
            }
            else if (matchingMethods.Count > 1)
            {
                // TODO: Test this case
                throw new NotImplementedException("Multiple matching methods");
            }
            var declaration = matchingMethods[0];

            // Assert that there is the right number of parameters
            if (call.Parameters.Count != declaration.ParameterTypes.Count)
            {
                diagnostics.Add(DiagnosticCode.ParameterCountMismatch, call.Position,
                                actual: call.Parameters.Count.ToString(), expected: declaration.ParameterTypes.Count.ToString());
                return(false);
            }

            // Evaluate the parameters, verifying their types
            var parameterIndices = new int[declaration.ParameterTypes.Count];

            for (var i = 0; i < parameterIndices.Length; i++)
            {
                var paramIndex = TryCompileExpression(call.Parameters[i], declaration.ParameterTypes[i],
                                                      method, builder, nameResolver, diagnostics);

                // If the compilation of the expression failed for some reason, the diagnostic is already logged
                if (paramIndex == -1)
                {
                    return(false);
                }
                parameterIndices[i] = paramIndex;
            }

            // Emit a call operation
            var callType = declaration is ImportedMethodDeclaration ? MethodCallType.Imported : MethodCallType.Native;

            var callInfoIndex = method.AddCallInfo(declaration.BodyIndex, parameterIndices, declaration.FullName, callType);
            var resultIndex   = method.AddLocal(declaration.ReturnType, LocalFlags.None);

            builder.AppendInstruction(Opcode.Call, callInfoIndex, 0, resultIndex);

            value = Temporary.FromLocal(declaration.ReturnType, resultIndex);
            return(true);
        }
Example #6
0
        //-------------------------------------------------------------------
        //  Constructors
        //-------------------------------------------------------------------
        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="DevicePortalViewModel" /> class.
        /// </summary>
        /// <param name="connection">IDevicePortalConnection used for connecting</param>
        /// <param name="diags">Diagnostic sink for reporting</param>
        public DevicePortalViewModel(IDevicePortalConnection connection, IDiagnosticSink diags)
            : base(connection, diags)
        {
            this.connectionStatus = DeviceConnectionStatus.None;

            // Add additional handling for untrusted certs.
            this.Portal.UnvalidatedCert += this.DoCertValidation;

            // Default number of retry attempts to make when reestablishing the connection
            this.ConnectionRetryAttempts = 3;
        }
Example #7
0
        private static bool ValidateEntryPoint(FunctionSyntax syntax, IDiagnosticSink diagnosticSink,
                                               TypeDefinition returnType, ImmutableList <TypeDefinition> parameterTypes)
        {
            // The method must return int32 and take no parameters
            if (returnType.Equals(SimpleType.Int32) && parameterTypes.Count == 0)
            {
                return(true);
            }

            diagnosticSink.Add(DiagnosticCode.EntryPointMustBeDeclaredCorrectly, syntax.Position);
            return(false);
        }
Example #8
0
        //-------------------------------------------------------------------
        // Constructors
        //-------------------------------------------------------------------
        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="DevicePortalCommandModel" /> class.
        /// </summary>
        /// <param name="connection">IDevicePortalConnection object used for connecting</param>
        /// <param name="diags">Diagnostic sink for reporting</param>
        public DevicePortalCommandModel(IDevicePortalConnection connection, IDiagnosticSink diags)
        {
            if (connection == null)
            {
                throw new ArgumentException("Must provide a valid IDevicePortalConnection object");
            }

            this.Connection = connection;
            this.Portal     = new DevicePortal(connection);

            this.diagnostics  = diags;
            this.commandQueue = new ObservableCommandQueue();
            this.Ready        = true;
        }
Example #9
0
        private static bool TryParseImportAttribute(AttributeSyntax attribute, IDiagnosticSink diagnosticSink,
                                                    [NotNullWhen(true)] out byte[]?nameBytes, [NotNullWhen(true)] out byte[]?libraryBytes)
        {
            nameBytes = libraryBytes = default;
            var isValid = true;

            // There must be exactly two string parameters: name and library
            if (attribute.Parameters.Count != 2)
            {
                diagnosticSink.Add(DiagnosticCode.ParameterCountMismatch, attribute.Position,
                                   attribute.Parameters.Count.ToString(), "2");
                return(false);
            }

            if (attribute.Parameters[0] is StringLiteralSyntax nameLiteral &&
                IsValidImportParameter(nameLiteral))
            {
                nameBytes = nameLiteral.Value;
            }
Example #10
0
        // TODO: Benchmark whether it would be beneficial to pass the unchanging parameters to the private methods
        //       in a readonly struct. We pass a lot of parameters and the call trees run quite deep.

        /// <summary>
        /// Compiles the given expression syntax tree and verifies its type.
        /// Returns the local index if successful, returns -1 and logs diagnostics if the compilation fails.
        /// </summary>
        /// <param name="syntax">The root of the expression syntax tree.</param>
        /// <param name="expectedType">The expected type of the evaluated expression. If null, the type is not checked.</param>
        /// <param name="method">The method to store and get local values from.</param>
        /// <param name="builder">Intermediate representation builder.</param>
        /// <param name="nameResolver">The resolver for variable and method names.</param>
        /// <param name="diagnostics">Receiver for possible diagnostics.</param>
        public static int TryCompileExpression(
            ExpressionSyntax syntax,
            TypeDefinition?expectedType,
            CompiledMethod method,
            BasicBlockBuilder builder,
            INameResolver nameResolver,
            IDiagnosticSink diagnostics)
        {
            // Compile the expression
            if (!InternalTryCompileExpression(syntax, method, builder, nameResolver, diagnostics, out var value))
            {
                return(-1);
            }

            // Verify the type
            // TODO: Once multiple integer types exist, there must be some notion of conversions
            if (expectedType != null && !value.Type.Equals(expectedType))
            {
                diagnostics.Add(DiagnosticCode.TypeMismatch, syntax.Position,
                                value.Type.TypeName, expectedType.TypeName);
                return(-1);
            }

            // Also, if the expression is constant, verify that it is representable
            if (!value.LocalIndex.HasValue && value.Type.Equals(SimpleType.Int32))
            {
                if (value.ConstantValue.AsSignedInteger < int.MinValue ||
                    value.ConstantValue.AsSignedInteger > int.MaxValue)
                {
                    diagnostics.Add(DiagnosticCode.IntegerConstantOutOfBounds, syntax.Position,
                                    value.ConstantValue.ToString(), SimpleType.Int32.TypeName);
                    return(-1);
                }
            }

            // Store the value in a local
            return(EnsureValueIsStored(value, method, builder));
        }
Example #11
0
        private static bool InternalTryCompileExpression(
            ExpressionSyntax syntax,
            CompiledMethod method,
            BasicBlockBuilder builder,
            INameResolver nameResolver,
            IDiagnosticSink diagnostics,
            out Temporary value)
        {
            value = default;

            if (syntax is IntegerLiteralSyntax integer)
            {
                // Decide the type for the literal
                // TODO: There is for now only support for int32 and uint32, and only because the smallest int32 should be
                // TODO: representable. This logic should be updated once multiple integer types officially exist.
                if (integer.Value > uint.MaxValue)
                {
                    diagnostics.Add(DiagnosticCode.IntegerConstantOutOfBounds, syntax.Position,
                                    integer.Value.ToString(), SimpleType.Int32.TypeName);
                    return(false);
                }
                else if (integer.Value > int.MaxValue)
                {
                    // TODO: This does not work for anything else than int32 minimum
                    value = Temporary.FromConstant(SimpleType.UInt32, ConstantValue.SignedInteger((long)integer.Value));
                    return(true);
                }
                else
                {
                    value = Temporary.FromConstant(SimpleType.Int32, ConstantValue.SignedInteger((long)integer.Value));
                    return(true);
                }
            }
            else if (syntax is BooleanLiteralSyntax boolean)
            {
                value = Temporary.FromConstant(SimpleType.Bool, ConstantValue.Bool(boolean.Value));
                return(true);
            }
            else if (syntax is IdentifierSyntax named)
            {
                if (!nameResolver.TryResolveVariable(named.Name, out var valueNumber))
                {
                    diagnostics.Add(DiagnosticCode.VariableNotFound, named.Position, named.Name);
                    return(false);
                }

                value = Temporary.FromLocal(method.Values[valueNumber].Type, (ushort)valueNumber);
                return(true);
            }
            else if (syntax is UnaryExpressionSyntax unary)
            {
                if (!InternalTryCompileExpression(unary.InnerExpression, method, builder, nameResolver, diagnostics,
                                                  out var inner))
                {
                    return(false);
                }

                return(TryCompileUnary(unary, inner, method, builder, diagnostics, out value));
            }
            else if (syntax is BinaryExpressionSyntax binary)
            {
                if (!InternalTryCompileExpression(binary.Left, method, builder, nameResolver, diagnostics, out var left) ||
                    !InternalTryCompileExpression(binary.Right, method, builder, nameResolver, diagnostics, out var right))
                {
                    return(false);
                }

                return(TryCompileBinary(binary, left, right, method, builder, diagnostics, out value));
            }
            else if (syntax is FunctionCallSyntax call)
            {
                return(TryCompileCall(call, method, builder, diagnostics, nameResolver, out value));
            }
            else
            {
                throw new NotImplementedException();
            }
        }
Example #12
0
        /// <summary>
        /// Verifies and creates type information for the method.
        /// Returns null if this fails, in which case diagnostics are also emitted.
        /// The name is not checked for duplication in this method.
        /// </summary>
        /// <param name="syntax">The syntax tree for the method.</param>
        /// <param name="definingNamespace">The name of the namespace this method is in.</param>
        /// <param name="definingFilename">The name of the file that contains the method.</param>
        /// <param name="methodBodyIndex">The index associated with the compiled method body.</param>
        /// <param name="declarationProvider">The type provider to use for resolving custom types.</param>
        /// <param name="diagnosticSink">The receiver for any semantic errors or warnings.</param>
        public static MethodDeclaration?Compile(
            FunctionSyntax syntax,
            string definingNamespace,
            string definingFilename,
            int methodBodyIndex,
            IDeclarationProvider declarationProvider,
            IDiagnosticSink diagnosticSink)
        {
            // Resolve the return type
            if (!TypeResolver.TryResolve(syntax.ReturnType, diagnosticSink, out var returnType))
            {
                return(null);
            }
            Debug.Assert(returnType != null);

            // Resolve parameter types
            // The parameter names are checked in InternalCompile()
            var parameterTypes = ImmutableList <TypeDefinition> .Empty;

            foreach (var param in syntax.Parameters)
            {
                if (!TypeResolver.TryResolve(param.Type, diagnosticSink, out var paramType))
                {
                    return(null);
                }
                Debug.Assert(paramType != null);
                if (paramType.Equals(SimpleType.Void))
                {
                    diagnosticSink.Add(DiagnosticCode.VoidIsNotValidType, param.Position, param.Name);
                    return(null);
                }
                parameterTypes = parameterTypes.Add(paramType);
            }

            // Apply the attributes
            // Each attribute can be validated independently, so don't stop on first failure
            var isEntryPoint = false;
            var isValid      = true;

            byte[]? importName    = null;
            byte[]? importLibrary = null;
            foreach (var attribute in syntax.Attributes)
            {
                switch (attribute.Name)
                {
                case "EntryPoint":
                    isValid     &= ValidateEntryPoint(syntax, diagnosticSink, returnType, parameterTypes);
                    isEntryPoint = true;
                    break;

                case "Import":
                    isValid &= TryParseImportAttribute(attribute, diagnosticSink, out importName, out importLibrary);
                    break;

                default:
                    diagnosticSink.Add(DiagnosticCode.UnknownAttribute, attribute.Position, attribute.Name);
                    isValid = false;
                    break;
                }
            }

            if (!isValid)
            {
                return(null);
            }

            // Some attributes may not be applied to the same method
            if (isEntryPoint && importName != null)
            {
                diagnosticSink.Add(DiagnosticCode.EntryPointAndImportNotCompatible, syntax.Position);
                return(null);
            }

            // Return a suitable subtype of the MethodDeclaration class
            if (importName != null)
            {
                Debug.Assert(importLibrary != null);

                return(new ImportedMethodDeclaration(methodBodyIndex, returnType, parameterTypes, syntax.Visibility,
                                                     definingNamespace + "::" + syntax.Name, definingFilename, syntax.Position, importName, importLibrary));
            }
            else
            {
                return(new NativeMethodDeclaration(methodBodyIndex, returnType, parameterTypes, syntax.Visibility,
                                                   definingNamespace + "::" + syntax.Name, definingFilename, syntax.Position, isEntryPoint));
            }
        }
Example #13
0
 /// <summary>
 /// Internal for testing only.
 /// Use <see cref="Parse"/> instead.
 /// </summary>
 internal SyntaxParser(Memory <byte> source, string filename, IDiagnosticSink diagnosticSink)
 {
     _lexer          = new Lexer(source);
     _filename       = filename;
     _diagnosticSink = diagnosticSink;
 }