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); }
/// <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); }
/// <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(); }
/// <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()); }
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); }
//------------------------------------------------------------------- // 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; }
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); }
//------------------------------------------------------------------- // 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; }
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; }
// 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)); }
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(); } }
/// <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)); } }
/// <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; }