public override TO2Type ResultType(IBlockContext context) { TO2Type targetType = target.ResultType(context); IFieldAccessEmitter fieldAccess = targetType.FindField(context.ModuleContext, fieldName)?.Create(context.ModuleContext); if (fieldAccess == null) { context.AddError(new StructuralError( StructuralError.ErrorType.NoSuchField, $"Type '{targetType.Name}' does not have a field '{fieldName}'", Start, End )); return(BuiltinType.Unit); } if (!fieldAccess.CanStore) { context.AddError(new StructuralError( StructuralError.ErrorType.NoSuchField, $"Type '{targetType.Name}' field '{fieldName}' is read-only", Start, End )); return(BuiltinType.Unit); } return(fieldAccess.FieldType); }
public override void EmitPtr(IBlockContext context) { TO2Type targetType = target.ResultType(context); IIndexAccessEmitter indexAccess = targetType.AllowedIndexAccess(context.ModuleContext, indexSpec); if (indexAccess == null) { context.AddError(new StructuralError( StructuralError.ErrorType.NoIndexAccess, $"Type '{targetType.Name}' does not support access by index", Start, End )); return; } if (indexAccess.RequiresPtr) { target.EmitPtr(context); } else { target.EmitCode(context, false); } if (context.HasErrors) { return; } indexAccess.EmitPtr(context); }
public void EmitCode(IBlockContext context) { TO2Type valueType = expression.ResultType(context); if (declaredReturn != BuiltinType.Unit && !declaredReturn.IsAssignableFrom(context.ModuleContext, valueType)) { context.AddError(new StructuralError( StructuralError.ErrorType.IncompatibleTypes, $"Function '{name}' returns {valueType} but should return {declaredReturn}", Start, End )); return; } if (isAsync) { EmitCodeAsync(context); } else { EmitCodeSync(context); } }
public static IDefaultValue ForParameter(IBlockContext context, FunctionParameter parameter) { if (parameter.defaultValue == null) { return(null); } switch (parameter.defaultValue) { case LiteralBool b when parameter.type == BuiltinType.Bool: return(new BoolDefaultValue(b.value)); case LiteralInt i when parameter.type == BuiltinType.Int: return(new IntDefaultValue(i.value)); case LiteralInt i when parameter.type == BuiltinType.Float: return(new FloatDefaultValue(i.value)); case LiteralFloat f when parameter.type == BuiltinType.Float: return(new FloatDefaultValue(f.value)); case LiteralString s when parameter.type == BuiltinType.String: return(new StringDefaultValue(s.value)); default: IBlockContext defaultContext = new SyncBlockContext(context.ModuleContext, FunctionModifier.Public, false, $"default_{context.MethodBuilder.Name}_{parameter.name}", parameter.type, new List <FunctionParameter>()); TO2Type resultType = parameter.defaultValue.ResultType(defaultContext); if (!parameter.type.IsAssignableFrom(context.ModuleContext, resultType)) { context.AddError(new StructuralError( StructuralError.ErrorType.IncompatibleTypes, $"Default value of parameter {parameter.name} has to be of type {parameter.type}, found {resultType}", parameter.Start, parameter.End )); return(null); } parameter.defaultValue.EmitCode(defaultContext, false); parameter.type.AssignFrom(context.ModuleContext, resultType).EmitConvert(context); defaultContext.IL.EmitReturn(parameter.type.GeneratedType(context.ModuleContext)); foreach (StructuralError error in defaultContext.AllErrors) { context.AddError(error); } return(new DefaultValueFactoryFunction(defaultContext.MethodBuilder)); } }
public override TO2Type ResultType(IBlockContext context) { TO2Type targetType = target.ResultType(context); IMethodInvokeFactory method = targetType.FindMethod(context.ModuleContext, methodName); if (method != null) { IMethodInvokeEmitter methodInvoker = method.Create(context, arguments.Select(arg => arg.ResultType(context)).ToList(), this); if (methodInvoker != null) { return(methodInvoker.ResultType); } } IFieldAccessFactory field = targetType.FindField(context.ModuleContext, methodName); if (field != null) { IFieldAccessEmitter fieldAccess = field.Create(context.ModuleContext); FunctionType functionType = fieldAccess.FieldType as FunctionType; if (functionType == null) { context.AddError(new StructuralError( StructuralError.ErrorType.NoSuchMethod, $"Field '{methodName}' of type '{targetType.Name}' is neither a method or a function", Start, End )); return(BuiltinType.Unit); } else { return(functionType.returnType); } } context.AddError(new StructuralError( StructuralError.ErrorType.NoSuchMethod, $"Type '{targetType.Name}' does not have a method or field '{methodName}'", Start, End )); return(BuiltinType.Unit); }
public override void EmitStore(IBlockContext context, IBlockVariable variable, bool dropResult) { if (!BuiltinType.Int.IsAssignableFrom(context.ModuleContext, from.ResultType(context))) { context.AddError(new StructuralError( StructuralError.ErrorType.IncompatibleTypes, "Range can only be created from int values", from.Start, from.End )); } if (!BuiltinType.Int.IsAssignableFrom(context.ModuleContext, to.ResultType(context))) { context.AddError(new StructuralError( StructuralError.ErrorType.IncompatibleTypes, "Range can only be created from int values", to.Start, to.End )); } if (context.HasErrors) { return; } variable.EmitLoadPtr(context); context.IL.Emit(OpCodes.Dup); from.EmitCode(context, false); context.IL.Emit(OpCodes.Stfld, typeof(Range).GetField("from")); to.EmitCode(context, false); if (inclusive) { context.IL.Emit(OpCodes.Ldc_I4_1); context.IL.Emit(OpCodes.Conv_I8); context.IL.Emit(OpCodes.Add); } context.IL.Emit(OpCodes.Stfld, typeof(Range).GetField("to")); if (!dropResult) { variable.EmitLoad(context); } }
public override void EmitCode(IBlockContext context, bool dropResult) { TO2Type leftType = left.ResultType(context); TO2Type rightType = right.ResultType(context); if (context.HasErrors) { return; } IOperatorEmitter leftEmitter = leftType.AllowedSuffixOperators(context.ModuleContext) .GetMatching(context.ModuleContext, op, rightType); IOperatorEmitter rightEmitter = rightType.AllowedPrefixOperators(context.ModuleContext) .GetMatching(context.ModuleContext, op, leftType); if (leftEmitter == null && rightEmitter == null) { context.AddError(new StructuralError( StructuralError.ErrorType.IncompatibleTypes, $"Cannot {op} a {leftType} with a {rightType}", Start, End )); return; } right.Prepare(context); left.EmitCode(context, false); rightEmitter?.OtherType.AssignFrom(context.ModuleContext, leftType).EmitConvert(context); right.EmitCode(context, false); leftEmitter?.OtherType.AssignFrom(context.ModuleContext, rightType).EmitConvert(context); if (context.HasErrors) { return; } if (leftEmitter != null) { leftEmitter.EmitCode(context, this); } else { rightEmitter.EmitCode(context, this); } if (dropResult) { context.IL.Emit(OpCodes.Pop); } }
public override void EmitCode(IBlockContext context, bool dropResult) { RealizedType sourceType = sourceExpression.ResultType(context).UnderlyingType(context.ModuleContext); IForInSource source = sourceType.ForInSource(context.ModuleContext, null); if (source == null) { context.AddError( new StructuralError( StructuralError.ErrorType.InvalidType, $"{sourceType} cannot be use as for ... in source", Start, End ) ); } foreach (DeclarationParameter declaration in declarations) { if (context.FindVariable(declaration.target) != null) { context.AddError(new StructuralError( StructuralError.ErrorType.DuplicateVariableName, $"Variable '{declaration.target}' already declared in this scope", Start, End )); } } if (context.HasErrors) { return; } switch (source !.ElementType) {
public override void EmitCode(IBlockContext context, bool dropResult) { if (!context.InnerLoop.HasValue) { context.AddError(new StructuralError( StructuralError.ErrorType.InvalidScope, "continue can only be used inside a loop", Start, End )); return; } context.IL.Emit(context.InnerLoop.Value.start.isShort ? OpCodes.Br_S : OpCodes.Br, context.InnerLoop.Value.start); }
public void EmitPtr(IBlockContext context) { TO2Type resultType = indexExpression.ResultType(context); if (!BuiltinType.Int.IsAssignableFrom(context.ModuleContext, resultType)) { context.AddError(new StructuralError( StructuralError.ErrorType.InvalidType, $"Index has to be of type {BuiltinType.Int}", indexExpression.Start, indexExpression.End )); return; } indexExpression.EmitCode(context, false); BuiltinType.Int.AssignFrom(context.ModuleContext, resultType).EmitConvert(context); context.IL.Emit(OpCodes.Conv_I4); context.IL.Emit(OpCodes.Ldelema, targetType.GeneratedType(context.ModuleContext)); }
private void EmitAssign(IBlockContext context, IBlockVariable blockVariable, TO2Type valueType, bool dropResult) { if (!blockVariable.Type.IsAssignableFrom(context.ModuleContext, valueType)) { context.AddError(new StructuralError( StructuralError.ErrorType.IncompatibleTypes, $"Variable '{name}' is of type {blockVariable.Type} but is assigned to {valueType}", Start, End )); } if (context.HasErrors) { return; } blockVariable.Type.AssignFrom(context.ModuleContext, valueType) .EmitAssign(context, blockVariable, expression, dropResult); }
public void EmitConstructor(IBlockContext context) { foreach (StructField field in fields.Where(e => e.IsRight).Select(e => e.Right)) { TO2Type initializerType = field.initializer.ResultType(context); if (!field.type.IsAssignableFrom(context.ModuleContext, initializerType)) { context.AddError(new StructuralError( StructuralError.ErrorType.IncompatibleTypes, $"Expected item {field.name} of {name} to be a {field.type}, found {initializerType}", Start, End )); } } if (context.HasErrors) { return; } Type type = typeDelegate.GeneratedType(context.ModuleContext); IBlockVariable variable = context.DeclaredVariable("instance", false, typeDelegate.UnderlyingType(context.ModuleContext)); variable.EmitLoad(context); foreach (StructField field in fields.Where(e => e.IsRight).Select(e => e.Right)) { context.IL.Emit(OpCodes.Dup); field.initializer.EmitCode(context, false); field.type.AssignFrom(context.ModuleContext, field.initializer.ResultType(context)) .EmitConvert(context); context.IL.Emit(OpCodes.Stfld, type.GetField(field.name)); } context.IL.EmitReturn(type); typeDelegate.CreateStructType(); }
public override void EmitCode(IBlockContext context, bool dropResult) { if (context.ExpectedReturn != BuiltinType.Unit) { context.AddError(new StructuralError( StructuralError.ErrorType.IncompatibleTypes, $"Expected a return value of type {context.ExpectedReturn}", Start, End )); return; } context.IL.Emit(OpCodes.Ldnull); if (context.IsAsync) { context.IL.EmitNew(OpCodes.Newobj, context.MethodBuilder.ReturnType.GetConstructor(new[] { typeof(object) })); } context.IL.EmitReturn(context.MethodBuilder.ReturnType); }
public void EmitStore(IBlockContext context, Action <IBlockContext> emitValue) { TO2Type resultType = indexExpression.ResultType(context); if (!BuiltinType.Int.IsAssignableFrom(context.ModuleContext, resultType)) { context.AddError(new StructuralError( StructuralError.ErrorType.InvalidType, $"Index has to be of type {BuiltinType.Int}", indexExpression.Start, indexExpression.End )); return; } indexExpression.EmitCode(context, false); BuiltinType.Int.AssignFrom(context.ModuleContext, resultType).EmitConvert(context); context.IL.Emit(OpCodes.Conv_I4); emitValue(context); if (targetType == BuiltinType.Bool) { context.IL.Emit(OpCodes.Stelem_I4); } else if (targetType == BuiltinType.Int) { context.IL.Emit(OpCodes.Stelem_I8); } else if (targetType == BuiltinType.Float) { context.IL.Emit(OpCodes.Stelem_R8); } else { context.IL.Emit(OpCodes.Stelem, targetType.GeneratedType(context.ModuleContext)); } }
public override TO2Type ResultType(IBlockContext context) { TO2Type leftType = left.ResultType(context); TO2Type rightType = right.ResultType(context); IOperatorEmitter operatorEmitter = leftType.AllowedSuffixOperators(context.ModuleContext) .GetMatching(context.ModuleContext, op, rightType) ?? rightType.AllowedPrefixOperators(context.ModuleContext) .GetMatching(context.ModuleContext, op, leftType); if (operatorEmitter == null) { context.AddError(new StructuralError( StructuralError.ErrorType.IncompatibleTypes, $"Cannot {op} a {leftType} with a {rightType}", Start, End )); return(BuiltinType.Unit); } return(operatorEmitter.ResultType); }
public override void EmitCode(IBlockContext context, bool dropResult) { if (preparedResult != null) { if (!dropResult) { preparedResult.EmitLoad(context); } preparedResult = null; return; } TO2Type targetType = target.ResultType(context); IMethodInvokeFactory method = targetType.FindMethod(context.ModuleContext, methodName); if (method != null) { EmitCodeMethodCall(context, targetType, method, dropResult); return; } IFieldAccessFactory field = targetType.FindField(context.ModuleContext, methodName); if (field != null) { EmitCodeDelegateCall(context, targetType, field, dropResult); return; } context.AddError(new StructuralError( StructuralError.ErrorType.NoSuchMethod, $"Type '{targetType.Name}' does not have a method or field '{methodName}'", Start, End )); }
public override void EmitCode(IBlockContext context, bool dropResult) { RealizedType sourceType = sourceExpression.ResultType(context).UnderlyingType(context.ModuleContext); IForInSource source = sourceType.ForInSource(context.ModuleContext, variableType); if (source == null) { context.AddError( new StructuralError( StructuralError.ErrorType.InvalidType, $"{sourceType} cannot be use as for ... in source", Start, End ) ); } if (context.FindVariable(variableName) != null) { context.AddError(new StructuralError( StructuralError.ErrorType.DuplicateVariableName, $"Variable '{variableName}' already declared in this scope", Start, End )); } if (source != null && variableType != null && !variableType.IsAssignableFrom(context.ModuleContext, source.ElementType)) { context.AddError( new StructuralError( StructuralError.ErrorType.InvalidType, $"{sourceType} has elements of type {source.ElementType}, expected {variableType}", Start, End ) ); } if (context.HasErrors) { return; } using ITempLocalRef loopCounter = context.IL.TempLocal(typeof(int)); ILCount loopSize = EstimateLoop(context, source); LabelRef start = context.IL.DefineLabel(loopSize.opCodes < 110); LabelRef end = context.IL.DefineLabel(loopSize.opCodes < 110); LabelRef loop = context.IL.DefineLabel(loopSize.opCodes < 100); IBlockContext loopContext = context.CreateLoopContext(start, end); IBlockVariable loopVariable = loopContext.DeclaredVariable(variableName, true, source !.ElementType); sourceExpression.EmitCode(context, false); if (context.HasErrors) { return; } source.EmitInitialize(loopContext); loopContext.IL.Emit(start.isShort ? OpCodes.Br_S : OpCodes.Br, start); loopContext.IL.MarkLabel(loop); // Timeout check LabelRef skipCheck = context.IL.DefineLabel(true); loopCounter.EmitLoad(loopContext); loopContext.IL.Emit(OpCodes.Ldc_I4_1); loopContext.IL.Emit(OpCodes.Add); loopContext.IL.Emit(OpCodes.Dup); loopCounter.EmitStore(loopContext); loopContext.IL.Emit(OpCodes.Ldc_I4, 10000); loopContext.IL.Emit(OpCodes.Cgt); loopContext.IL.Emit(OpCodes.Brfalse, skipCheck); loopContext.IL.Emit(OpCodes.Ldc_I4_0); loopCounter.EmitStore(loopContext); context.IL.EmitCall(OpCodes.Call, typeof(Runtime.ContextHolder).GetMethod("CheckTimeout"), 0); loopContext.IL.MarkLabel(skipCheck); source.EmitNext(loopContext); loopVariable.EmitStore(loopContext); loopExpression.EmitCode(loopContext, true); loopContext.IL.MarkLabel(start); source.EmitCheckDone(loopContext, loop); loopContext.IL.MarkLabel(end); if (!dropResult) { context.IL.Emit(OpCodes.Ldnull); } }
public override void EmitCode(IBlockContext context, bool dropResult) { if (condition.ResultType(context) != BuiltinType.Bool) { context.AddError( new StructuralError( StructuralError.ErrorType.InvalidType, "Condition of if is not a boolean", Start, End ) ); return; } IBlockContext thenContext = context.CreateChildContext(); IBlockContext elseContext = context.CreateChildContext(); Dictionary <string, TO2Type> scopeVariables = condition.GetScopeVariables(thenContext); if (scopeVariables != null) { foreach (var(name, type) in scopeVariables) { if (thenContext.FindVariable(name) != null) { thenContext.AddError(new StructuralError( StructuralError.ErrorType.DuplicateVariableName, $"Variable '{name}' already declared in this scope", Start, End )); return; } thenContext.DeclaredVariable(name, true, type.UnderlyingType(context.ModuleContext)); } } ILCount thenCount = thenExpression.GetILCount(thenContext, dropResult); ILCount elseCount = elseExpression.GetILCount(elseContext, dropResult); if (!context.HasErrors && thenCount.stack > 1) { context.AddError( new StructuralError( StructuralError.ErrorType.CoreGeneration, "Then expression leaves too many values on stack. This must not happen", Start, End ) ); return; } if (!context.HasErrors && elseCount.stack > 1) { context.AddError( new StructuralError( StructuralError.ErrorType.CoreGeneration, "Else expression leaves too many values on stack. This must not happen", Start, End ) ); return; } condition.EmitCode(thenContext, false); if (context.HasErrors) { return; } TO2Type thenType = thenExpression.ResultType(thenContext); TO2Type elseType = elseExpression.ResultType(elseContext); if (!dropResult) { if (!thenType.IsAssignableFrom(context.ModuleContext, elseType)) { context.AddError(new StructuralError( StructuralError.ErrorType.IncompatibleTypes, $"If condition has incompatible result {thenType} != {elseType}", Start, End )); } } if (context.HasErrors) { return; } LabelRef thenEnd = context.IL.DefineLabel(thenCount.opCodes < 124); LabelRef elseEnd = context.IL.DefineLabel(elseCount.opCodes < 124); context.IL.Emit(thenEnd.isShort ? OpCodes.Brfalse_S : OpCodes.Brfalse, thenEnd); thenExpression.EmitCode(thenContext, dropResult); context.IL.Emit(elseEnd.isShort ? OpCodes.Br_S : OpCodes.Br, elseEnd); context.IL.MarkLabel(thenEnd); if (!dropResult) { context.IL.AdjustStack(-1); // Then leave its result on the stack, so is else supposed to } elseExpression.EmitCode(elseContext, dropResult); if (!dropResult) { thenType.AssignFrom(context.ModuleContext, elseType).EmitConvert(context); } context.IL.MarkLabel(elseEnd); }
private void EmitCodeDelegateCall(IBlockContext context, TO2Type targetType, IFieldAccessFactory field, bool dropResult) { IFieldAccessEmitter fieldAccess = field.Create(context.ModuleContext); FunctionType functionType = fieldAccess.FieldType as FunctionType; if (functionType == null) { context.AddError(new StructuralError( StructuralError.ErrorType.NoSuchMethod, $"Field '{methodName}' of type '{targetType.Name}' is neither a method or a function", Start, End )); return; } if (functionType.isAsync && !context.IsAsync) { context.AddError(new StructuralError( StructuralError.ErrorType.NoSuchFunction, $"Cannot call async function of variable {methodName}' of type '{targetType.Name} from a sync context", Start, End )); return; } if (functionType.parameterTypes.Count != arguments.Count) { context.AddError(new StructuralError( StructuralError.ErrorType.ArgumentMismatch, $"Call to '{methodName}' of type '{targetType.Name}' requires {functionType.parameterTypes.Count} arguments", Start, End )); return; } if (fieldAccess.RequiresPtr) { target.EmitPtr(context); } else { target.EmitCode(context, false); } fieldAccess.EmitLoad(context); for (int i = 0; i < arguments.Count; i++) { arguments[i].EmitCode(context, false); if (!context.HasErrors) { functionType.parameterTypes[i].AssignFrom(context.ModuleContext, arguments[i].ResultType(context)) .EmitConvert(context); } } if (context.HasErrors) { return; } for (int i = 0; i < arguments.Count; i++) { TO2Type argumentType = arguments[i].ResultType(context); if (!functionType.parameterTypes[i].IsAssignableFrom(context.ModuleContext, argumentType)) { context.AddError(new StructuralError( StructuralError.ErrorType.ArgumentMismatch, $"Argument {i + 1} of '{methodName}' of type '{targetType.Name}' has to be a {functionType.parameterTypes[i]}, but {argumentType} was given", Start, End )); return; } } MethodInfo invokeMethod = functionType.GeneratedType(context.ModuleContext).GetMethod("Invoke") ?? throw new ArgumentException($"No Invoke method in generated ${functionType}"); context.IL.EmitCall(OpCodes.Callvirt, invokeMethod, arguments.Count + 1); if (functionType.isAsync) { context.RegisterAsyncResume(functionType.returnType); } if (dropResult && invokeMethod.ReturnType != typeof(void)) { context.IL.Emit(OpCodes.Pop); } if (!dropResult && invokeMethod.ReturnType == typeof(void)) { context.IL.Emit(OpCodes.Ldnull); } }
private void EmitCodeMethodCall(IBlockContext context, TO2Type targetType, IMethodInvokeFactory method, bool dropResult) { if (target is IAssignContext assignContext) { if (assignContext.IsConst(context) && !method.IsConst) { context.AddError(new StructuralError( StructuralError.ErrorType.NoSuchMethod, $"Method '{methodName}' will mutate const variable.", Start, End )); } } List <TO2Type> argumentTypes = arguments.Select(arg => arg.ResultType(context)).ToList(); IMethodInvokeEmitter methodInvoker = method.Create(context, argumentTypes, this); if (methodInvoker == null) { context.AddError(new StructuralError( StructuralError.ErrorType.NoSuchMethod, $"Type '{targetType.Name}' does not have a method '{methodName}' matching arguments ({string.Join(", ", argumentTypes)})", Start, End )); return; } if (methodInvoker.IsAsync && !context.IsAsync) { context.AddError(new StructuralError( StructuralError.ErrorType.NoSuchFunction, $"Cannot call async method of variable '{targetType.Name}.{methodName}' from a sync context", Start, End )); return; } if (methodInvoker.RequiredParameterCount() > arguments.Count) { context.AddError(new StructuralError( StructuralError.ErrorType.ArgumentMismatch, $"Method '{targetType.Name}.{methodName}' requires {methodInvoker.RequiredParameterCount()} arguments", Start, End )); return; } int i; for (i = 0; i < arguments.Count; i++) { TO2Type argumentType = arguments[i].ResultType(context); if (!methodInvoker.Parameters[i].type.IsAssignableFrom(context.ModuleContext, argumentType)) { context.AddError(new StructuralError( StructuralError.ErrorType.ArgumentMismatch, $"Argument {methodInvoker.Parameters[i].name} of '{targetType.Name}.{methodName}' has to be a {methodInvoker.Parameters[i].type}, but got {argumentType}", Start, End )); return; } } foreach (Expression argument in arguments) { argument.Prepare(context); } if (methodInvoker.RequiresPtr) { target.EmitPtr(context); } else { target.EmitCode(context, false); } for (i = 0; i < arguments.Count; i++) { arguments[i].EmitCode(context, false); if (!context.HasErrors) { methodInvoker.Parameters[i].type.AssignFrom(context.ModuleContext, arguments[i].ResultType(context)) .EmitConvert(context); } } if (!context.HasErrors) { for (; i < methodInvoker.Parameters.Count; i++) { methodInvoker.Parameters[i].defaultValue.EmitCode(context); } } if (context.HasErrors) { return; } methodInvoker.EmitCode(context); if (methodInvoker.IsAsync) { context.RegisterAsyncResume(methodInvoker.ResultType); } if (dropResult) { context.IL.Emit(OpCodes.Pop); } }
internal static AsyncClass Create(IBlockContext parent, string name, TO2Type declaredReturn, List <FunctionParameter> parameters, Expression expression) { Type returnType = declaredReturn.GeneratedType(parent.ModuleContext); Type typeParameter = returnType == typeof(void) ? typeof(object) : returnType; ModuleContext asyncModuleContext = parent.ModuleContext.DefineSubContext($"AsyncFunction_{name}", typeof(Future <>).MakeGenericType(typeParameter)); List <ClonedFieldVariable> clonedParameters = new List <ClonedFieldVariable>(); foreach (FunctionParameter parameter in parameters) { FieldBuilder field = asyncModuleContext.typeBuilder.DefineField(parameter.name, parameter.type.GeneratedType(parent.ModuleContext), FieldAttributes.Private); clonedParameters.Add( new ClonedFieldVariable(parameter.type.UnderlyingType(parent.ModuleContext), field)); } // ------------- PollValue ------------- AsyncBlockContext asyncContext = new AsyncBlockContext(asyncModuleContext, FunctionModifier.Public, "PollValue", declaredReturn, typeof(FutureResult <>).MakeGenericType(typeParameter), clonedParameters); LabelRef applyState = asyncContext.IL.DefineLabel(false); LabelRef initialState = asyncContext.IL.DefineLabel(false); asyncContext.IL.Emit(OpCodes.Br, applyState); asyncContext.IL.MarkLabel(initialState); expression.EmitCode(asyncContext, false); if (!asyncContext.HasErrors) { declaredReturn.AssignFrom(asyncContext.ModuleContext, expression.ResultType(asyncContext)) .EmitConvert(asyncContext); } asyncContext.IL.EmitNew(OpCodes.Newobj, asyncContext.MethodBuilder.ReturnType.GetConstructor(new[] { typeParameter })); asyncContext.IL.EmitReturn(asyncContext.MethodBuilder.ReturnType); // Apply state asyncContext.IL.MarkLabel(applyState); asyncContext.IL.Emit(OpCodes.Ldarg_0); asyncContext.IL.Emit(OpCodes.Ldfld, asyncContext.stateField); asyncContext.IL.Emit(OpCodes.Switch, initialState.Yield().Concat(asyncContext.asyncResumes.Select(ar => ar.pollLabel))); asyncContext.IL.Emit(OpCodes.Ldarg_0); asyncContext.IL.Emit(OpCodes.Ldfld, asyncContext.stateField); asyncContext.IL.EmitNew(OpCodes.Newobj, typeof(InvalidAsyncStateException).GetConstructor(new[] { typeof(int) }), 1); asyncContext.IL.Emit(OpCodes.Throw); foreach (AsyncResume asyncResume in asyncContext.asyncResumes) { asyncResume.EmitPoll(asyncContext); } // Restore state asyncContext.IL.MarkLabel(asyncContext.resume); foreach (StateRef stateRef in asyncContext.stateRefs) { stateRef.EmitRestore(asyncContext); } asyncContext.IL.Emit(OpCodes.Ldarg_0); asyncContext.IL.Emit(OpCodes.Ldfld, asyncContext.stateField); asyncContext.IL.Emit(OpCodes.Switch, initialState.Yield().Concat(asyncContext.asyncResumes.Select(ar => ar.resumeLabel))); asyncContext.IL.Emit(OpCodes.Ldarg_0); asyncContext.IL.Emit(OpCodes.Ldfld, asyncContext.stateField); asyncContext.IL.EmitNew(OpCodes.Newobj, typeof(InvalidAsyncStateException).GetConstructor(new[] { typeof(int) }), 1); asyncContext.IL.Emit(OpCodes.Throw); // Store state asyncContext.IL.MarkLabel(asyncContext.storeState); foreach (StateRef stateRef in asyncContext.stateRefs) { stateRef.EmitStore(asyncContext); } asyncContext.IL.MarkLabel(asyncContext.notReady); using (ITempLocalRef notReady = asyncContext.IL.TempLocal(asyncContext.MethodBuilder.ReturnType)) { notReady.EmitLoadPtr(asyncContext); asyncContext.IL.Emit(OpCodes.Initobj, asyncContext.MethodBuilder.ReturnType, 1, 0); notReady.EmitLoad(asyncContext); asyncContext.IL.EmitReturn(asyncContext.MethodBuilder.ReturnType); } foreach (StructuralError error in asyncContext.AllErrors) { parent.AddError(error); } // ------------- Constructor ------------- List <FieldInfo> parameterFields = clonedParameters.Select(c => c.valueField).ToList(); ConstructorBuilder constructorBuilder = asyncModuleContext.typeBuilder.DefineConstructor( MethodAttributes.Public, CallingConventions.Standard, parameterFields.Select(f => f.FieldType).ToArray()); IILEmitter constructorEmitter = new GeneratorILEmitter(constructorBuilder.GetILGenerator()); int argIndex = 1; foreach (FieldInfo field in parameterFields) { constructorEmitter.Emit(OpCodes.Ldarg_0); MethodParameter.EmitLoadArg(constructorEmitter, argIndex++); constructorEmitter.Emit(OpCodes.Stfld, field); } constructorEmitter.Emit(OpCodes.Ldarg_0); constructorEmitter.Emit(OpCodes.Ldc_I4_0); constructorEmitter.Emit(OpCodes.Stfld, asyncContext.stateField); constructorEmitter.EmitReturn(typeof(void)); return(new AsyncClass(asyncModuleContext.typeBuilder, constructorBuilder)); }
public override void EmitCode(IBlockContext context, bool dropResult) { if (condition.ResultType(context) != BuiltinType.Bool) { context.AddError( new StructuralError( StructuralError.ErrorType.InvalidType, "Condition of while is not a boolean", Start, End ) ); } IBlockContext tmpContext = context.CreateLoopContext(context.IL.DefineLabel(false), context.IL.DefineLabel(false)); Dictionary <string, TO2Type> scopeVariables = condition.GetScopeVariables(tmpContext); if (scopeVariables != null) { foreach (var(name, type) in scopeVariables) { tmpContext.DeclaredVariable(name, true, type.UnderlyingType(context.ModuleContext)); } } ILCount conditionCount = condition.GetILCount(tmpContext, false); ILCount loopCount = loopExpression.GetILCount(tmpContext, true); if (loopCount.stack > 0) { context.AddError( new StructuralError( StructuralError.ErrorType.CoreGeneration, "Body of the while expression leaves values on stack. This must not happen", Start, End ) ); return; } if (context.HasErrors) { return; } using ITempLocalRef loopCounter = context.IL.TempLocal(typeof(int)); LabelRef whileStart = context.IL.DefineLabel(conditionCount.opCodes + loopCount.opCodes < 110); LabelRef whileEnd = context.IL.DefineLabel(conditionCount.opCodes + loopCount.opCodes < 110); LabelRef whileLoop = context.IL.DefineLabel(conditionCount.opCodes + loopCount.opCodes < 100); IBlockContext loopContext = context.CreateLoopContext(whileStart, whileEnd); if (scopeVariables != null) { foreach (var(name, type) in scopeVariables) { if (loopContext.FindVariable(name) != null) { loopContext.AddError(new StructuralError( StructuralError.ErrorType.DuplicateVariableName, $"Variable '{name}' already declared in this scope", Start, End )); return; } loopContext.DeclaredVariable(name, true, type.UnderlyingType(context.ModuleContext)); } } loopContext.IL.Emit(whileStart.isShort ? OpCodes.Br_S : OpCodes.Br, whileStart); context.IL.MarkLabel(whileLoop); // Timeout check LabelRef skipCheck = context.IL.DefineLabel(true); loopCounter.EmitLoad(loopContext); loopContext.IL.Emit(OpCodes.Ldc_I4_1); loopContext.IL.Emit(OpCodes.Add); loopContext.IL.Emit(OpCodes.Dup); loopCounter.EmitStore(loopContext); loopContext.IL.Emit(OpCodes.Ldc_I4, 10000); loopContext.IL.Emit(OpCodes.Cgt); loopContext.IL.Emit(OpCodes.Brfalse, skipCheck); loopContext.IL.Emit(OpCodes.Ldc_I4_0); loopCounter.EmitStore(loopContext); context.IL.EmitCall(OpCodes.Call, typeof(Runtime.ContextHolder).GetMethod("CheckTimeout"), 0); loopContext.IL.MarkLabel(skipCheck); loopExpression.EmitCode(loopContext, true); loopContext.IL.MarkLabel(whileStart); condition.EmitCode(loopContext, false); loopContext.IL.Emit(whileLoop.isShort ? OpCodes.Brtrue_S : OpCodes.Brtrue, whileLoop); loopContext.IL.MarkLabel(whileEnd); if (!dropResult) { context.IL.Emit(OpCodes.Ldnull); } }
public override void EmitCode(IBlockContext context, bool dropResult) { if (condition.ResultType(context) != BuiltinType.Bool) { context.AddError( new StructuralError( StructuralError.ErrorType.InvalidType, "Condition of if is not a boolean", Start, End ) ); return; } IBlockContext thenContext = context.CreateChildContext(); Dictionary <string, TO2Type> scopeVariables = condition.GetScopeVariables(thenContext); if (scopeVariables != null) { foreach (var(name, type) in scopeVariables) { if (thenContext.FindVariable(name) != null) { thenContext.AddError(new StructuralError( StructuralError.ErrorType.DuplicateVariableName, $"Variable '{name}' already declared in this scope", Start, End )); return; } thenContext.DeclaredVariable(name, true, type.UnderlyingType(context.ModuleContext)); } } ILCount thenCount = thenExpression.GetILCount(thenContext, true); if (!context.HasErrors && thenCount.stack > 0) { context.AddError( new StructuralError( StructuralError.ErrorType.CoreGeneration, "Then expression leaves values on stack. This must not happen", Start, End ) ); return; } condition.EmitCode(thenContext, false); if (context.HasErrors) { return; } TO2Type thenResultType = thenExpression.ResultType(thenContext); if (dropResult) { LabelRef skipThen = context.IL.DefineLabel(thenCount.opCodes < 124); thenContext.IL.Emit(skipThen.isShort ? OpCodes.Brfalse_S : OpCodes.Brfalse, skipThen); thenExpression.EmitCode(thenContext, true); thenContext.IL.MarkLabel(skipThen); } else { OptionType optionType = new OptionType(thenResultType); Type generatedType = optionType.GeneratedType(thenContext.ModuleContext); using ITempLocalRef tempResult = thenContext.IL.TempLocal(generatedType); LabelRef skipThen = thenContext.IL.DefineLabel(thenCount.opCodes < 114); thenContext.IL.Emit(skipThen.isShort ? OpCodes.Brfalse_S : OpCodes.Brfalse, skipThen); thenExpression.Prepare(thenContext); tempResult.EmitLoadPtr(context); thenContext.IL.Emit(OpCodes.Dup); thenContext.IL.Emit(OpCodes.Initobj, generatedType, 1, 0); thenContext.IL.Emit(OpCodes.Dup); thenContext.IL.Emit(OpCodes.Ldc_I4_1); thenContext.IL.Emit(OpCodes.Stfld, generatedType.GetField("defined")); thenExpression.EmitCode(thenContext, false); thenContext.IL.Emit(OpCodes.Stfld, generatedType.GetField("value")); LabelRef ifEnd = context.IL.DefineLabel(true); thenContext.IL.Emit(OpCodes.Br_S, ifEnd); thenContext.IL.MarkLabel(skipThen); tempResult.EmitLoadPtr(context); thenContext.IL.Emit(OpCodes.Initobj, generatedType, 1, 0); thenContext.IL.MarkLabel(ifEnd); tempResult.EmitLoad(thenContext); } }
public override void EmitCode(IBlockContext context, bool dropResult) { TO2Type targetType = target.ResultType(context); IFieldAccessEmitter fieldAccess = targetType.FindField(context.ModuleContext, fieldName)?.Create(context.ModuleContext); TO2Type valueType = expression.ResultType(context); if (fieldAccess == null) { context.AddError(new StructuralError( StructuralError.ErrorType.NoSuchField, $"Type '{targetType.Name}' does not have a field '{fieldName}'", Start, End )); return; } if (!fieldAccess.CanStore) { context.AddError(new StructuralError( StructuralError.ErrorType.NoSuchField, $"Type '{targetType.Name}' field '{fieldName}' is read-only", Start, End )); return; } if (target is IAssignContext assignContext) { if (fieldAccess.RequiresPtr && assignContext.IsConst(context)) { context.AddError(new StructuralError( StructuralError.ErrorType.NoSuchField, $"Type '{targetType.Name}' field '{fieldName}' can not be set on a read-only variable", Start, End )); return; } } else { context.AddError(new StructuralError( StructuralError.ErrorType.CoreGeneration, $"Field assign '{targetType.Name}'.'{fieldName}' on invalid target expression", Start, End )); return; } if (!fieldAccess.FieldType.IsAssignableFrom(context.ModuleContext, valueType)) { context.AddError(new StructuralError( StructuralError.ErrorType.IncompatibleTypes, $"Type '{targetType.Name}' field '{fieldName}' is of type {fieldAccess.FieldType} but is assigned to {valueType}", Start, End )); return; } if (op == Operator.Assign) { if (fieldAccess.RequiresPtr) { target.EmitPtr(context); } else { target.EmitCode(context, false); } if (context.HasErrors) { return; } expression.EmitCode(context, false); fieldAccess.FieldType.AssignFrom(context.ModuleContext, valueType).EmitConvert(context); if (!dropResult) { using ITempBlockVariable tmpResult = context.MakeTempVariable(fieldAccess.FieldType); context.IL.Emit(OpCodes.Dup); tmpResult.EmitStore(context); fieldAccess.EmitStore(context); tmpResult.EmitLoad(context); } else { fieldAccess.EmitStore(context); } } else { IOperatorEmitter operatorEmitter = fieldAccess.FieldType.AllowedSuffixOperators(context.ModuleContext) .GetMatching(context.ModuleContext, op, valueType); if (operatorEmitter == null) { context.AddError(new StructuralError( StructuralError.ErrorType.IncompatibleTypes, $"Type '{targetType.Name}' field '{fieldName}': Cannot {op} a {fieldAccess.FieldType} with a {valueType}", Start, End )); return; } expression.Prepare(context); if (fieldAccess.RequiresPtr) { target.EmitPtr(context); } else { target.EmitCode(context, false); } if (fieldAccess.RequiresPtr) { target.EmitPtr(context); } else { target.EmitCode(context, false); } if (context.HasErrors) { return; } fieldAccess.EmitLoad(context); expression.EmitCode(context, false); operatorEmitter.OtherType.AssignFrom(context.ModuleContext, valueType).EmitConvert(context); operatorEmitter.EmitCode(context, this); if (!dropResult) { using ITempBlockVariable tmpResult = context.MakeTempVariable(fieldAccess.FieldType); context.IL.Emit(OpCodes.Dup); tmpResult.EmitStore(context); fieldAccess.EmitStore(context); tmpResult.EmitLoad(context); } else { fieldAccess.EmitStore(context); } } }
public override void EmitCode(IBlockContext context, bool dropResult) { TO2Type valueType = expression.ResultType(context); TO2Type targetType = target.ResultType(context); IIndexAccessEmitter indexAccess = targetType.AllowedIndexAccess(context.ModuleContext, indexSpec); if (indexAccess == null) { context.AddError(new StructuralError( StructuralError.ErrorType.NoIndexAccess, $"Type '{targetType.Name}' does not support access by index", Start, End )); return; } if (target is IAssignContext assignContext) { if (assignContext.IsConst(context)) { context.AddError(new StructuralError( StructuralError.ErrorType.NoSuchField, $"Type '{targetType.Name}' elements can not be set on a read-only variable", Start, End )); return; } } else { context.AddError(new StructuralError( StructuralError.ErrorType.CoreGeneration, $"Index assign '{targetType.Name}'.'{indexSpec}' on invalid target expression", Start, End )); return; } if (op == Operator.Assign) { if (indexAccess.RequiresPtr) { target.EmitPtr(context); } else { target.EmitCode(context, false); } if (context.HasErrors) { return; } if (!dropResult) { using ITempBlockVariable tmpResult = context.MakeTempVariable(indexAccess.TargetType.UnderlyingType(context.ModuleContext)); indexAccess.EmitStore(context, subContext => { expression.EmitCode(subContext, false); indexAccess.TargetType.AssignFrom(subContext.ModuleContext, valueType) .EmitConvert(subContext); context.IL.Emit(OpCodes.Dup); // ReSharper disable once AccessToDisposedClosure tmpResult.EmitStore(subContext); }); tmpResult.EmitLoad(context); } else { indexAccess.EmitStore(context, subContext => { expression.EmitCode(subContext, false); indexAccess.TargetType.AssignFrom(subContext.ModuleContext, valueType).EmitConvert(subContext); }); } } else { IOperatorEmitter operatorEmitter = indexAccess.TargetType.AllowedSuffixOperators(context.ModuleContext) .GetMatching(context.ModuleContext, op, valueType); if (operatorEmitter == null) { context.AddError(new StructuralError( StructuralError.ErrorType.IncompatibleTypes, $"Index assign '{targetType.Name}'.'{indexSpec}': Cannot {op} a {indexAccess.TargetType} with a {valueType}", Start, End )); return; } expression.Prepare(context); if (indexAccess.RequiresPtr) { target.EmitPtr(context); } else { target.EmitCode(context, false); } if (context.HasErrors) { return; } if (!dropResult) { using ITempBlockVariable tmpResult = context.MakeTempVariable(indexAccess.TargetType.UnderlyingType(context.ModuleContext)); indexAccess.EmitStore(context, subContext => { if (indexAccess.RequiresPtr) { target.EmitPtr(context); } else { target.EmitCode(context, false); } indexAccess.EmitLoad(context); expression.EmitCode(subContext, false); operatorEmitter.OtherType.AssignFrom(context.ModuleContext, valueType).EmitConvert(context); operatorEmitter.EmitCode(context, this); context.IL.Emit(OpCodes.Dup); // ReSharper disable once AccessToDisposedClosure tmpResult.EmitStore(subContext); }); tmpResult.EmitLoad(context); } else { indexAccess.EmitStore(context, subContext => { if (indexAccess.RequiresPtr) { target.EmitPtr(context); } else { target.EmitCode(context, false); } indexAccess.EmitLoad(context); expression.EmitCode(subContext, false); operatorEmitter.OtherType.AssignFrom(context.ModuleContext, valueType).EmitConvert(context); operatorEmitter.EmitCode(context, this); }); } } }
public override void EmitCode(IBlockContext context, bool dropResult) { IBlockVariable blockVariable = context.FindVariable(name); if (blockVariable == null) { context.AddError(new StructuralError( StructuralError.ErrorType.NoSuchVariable, $"No local variable '{name}'", Start, End )); } else if (blockVariable.IsConst) { context.AddError(new StructuralError( StructuralError.ErrorType.NoSuchVariable, $"Local variable '{name}' is read-only (const)", Start, End )); } if (context.HasErrors) { return; } TO2Type valueType = expression.ResultType(context); if (context.HasErrors) { return; } if (op == Operator.Assign) { EmitAssign(context, blockVariable, valueType, dropResult); return; } IOperatorEmitter operatorEmitter = blockVariable !.Type.AllowedSuffixOperators(context.ModuleContext) .GetMatching(context.ModuleContext, op, valueType); if (operatorEmitter == null) { context.AddError(new StructuralError( StructuralError.ErrorType.IncompatibleTypes, $"Cannot {op} a {blockVariable.Type} with a {valueType}", Start, End )); return; } expression.Prepare(context); blockVariable.EmitLoad(context); expression.EmitCode(context, false); if (context.HasErrors) { return; } operatorEmitter.OtherType.AssignFrom(context.ModuleContext, valueType).EmitConvert(context); operatorEmitter.EmitAssign(context, blockVariable, this); if (!dropResult) { blockVariable.EmitLoad(context); } }
public override void EmitCode(IBlockContext context, bool dropResult) { TO2Type leftType = left.ResultType(context); TO2Type rightType = right.ResultType(context); if (leftType != BuiltinType.Bool) { context.AddError(new StructuralError( StructuralError.ErrorType.IncompatibleTypes, "Expected boolean", left.Start, left.End )); } if (rightType != BuiltinType.Bool) { context.AddError(new StructuralError( StructuralError.ErrorType.IncompatibleTypes, "Expected boolean", right.Start, right.End )); } if (context.HasErrors) { return; } left.EmitCode(context, false); if (!dropResult) { context.IL.Emit(OpCodes.Dup); } ILCount rightCount = right.GetILCount(context, dropResult); LabelRef skipRight = context.IL.DefineLabel(rightCount.opCodes < 124); if (context.HasErrors) { return; } switch (op) { case Operator.BoolAnd: context.IL.Emit(skipRight.isShort ? OpCodes.Brfalse_S : OpCodes.Brfalse, skipRight); break; case Operator.BoolOr: context.IL.Emit(skipRight.isShort ? OpCodes.Brtrue_S : OpCodes.Brtrue, skipRight); break; default: context.AddError(new StructuralError( StructuralError.ErrorType.InvalidOperator, $"Invalid boolean operator {op}", Start, End )); return; } if (!dropResult) { context.IL.Emit(OpCodes.Pop); } right.EmitCode(context, dropResult); context.IL.MarkLabel(skipRight); }
public override void EmitStore(IBlockContext context, IBlockVariable variable, bool dropResult) { TupleType tupleType = variable.Type as TupleType; if (tupleType == null) { context.AddError(new StructuralError( StructuralError.ErrorType.InvalidType, $"{variable.Type} is not a tuple", Start, End )); return; } else { if (items.Count != tupleType.itemTypes.Count) { context.AddError(new StructuralError( StructuralError.ErrorType.InvalidType, $"Expected tuple of {tupleType.itemTypes.Count} items, found {items.Count} items", Start, End )); } for (int i = 0; i < items.Count; i++) { TO2Type valueType = items[i].ResultType(context); if (!tupleType.itemTypes[i].IsAssignableFrom(context.ModuleContext, valueType)) { context.AddError(new StructuralError( StructuralError.ErrorType.InvalidType, $"Expected item {i} of {tupleType} to be a {tupleType.itemTypes[i]}, found {valueType}", Start, End )); } } } if (context.HasErrors) { return; } foreach (Expression item in items) { item.Prepare(context); } Type type = tupleType.GeneratedType(context.ModuleContext); variable.EmitLoadPtr(context); // Note: Potentially overoptimized: Since all fields will be set, initialization should not be necessary // context.IL.Emit(OpCodes.Dup); // context.IL.Emit(OpCodes.Initobj, type, 1, 0); for (int i = 0; i < items.Count; i++) { if (i > 0 && i % 7 == 0) { context.IL.Emit(OpCodes.Ldflda, type.GetField("Rest")); type = type.GetGenericArguments()[7]; // context.IL.Emit(OpCodes.Dup); // context.IL.Emit(OpCodes.Initobj, type, 1, 0); } if (i < items.Count - 1) { context.IL.Emit(OpCodes.Dup); } items[i].EmitCode(context, false); tupleType.itemTypes[i].AssignFrom(context.ModuleContext, items[i].ResultType(context)) .EmitConvert(context); context.IL.Emit(OpCodes.Stfld, type.GetField($"Item{i % 7 + 1}")); } if (context.HasErrors) { return; } if (!dropResult) { variable.EmitLoad(context); } }
public override void EmitCode(IBlockContext context, bool dropResult) { if (dropResult) { return; } ArrayType arrayType = ResultType(context) as ArrayType; if (arrayType == null) { context.AddError(new StructuralError( StructuralError.ErrorType.InvalidType, "Unable to infer type of array. Please add some type hint", Start, End )); return; } RealizedType elementType = arrayType.ElementType.UnderlyingType(context.ModuleContext); context.IL.Emit(OpCodes.Ldc_I4, Elements.Count); context.IL.Emit(OpCodes.Newarr, elementType.GeneratedType(context.ModuleContext)); foreach (var element in Elements) { TO2Type valueType = element.ResultType(context); if (!elementType.IsAssignableFrom(context.ModuleContext, valueType)) { context.AddError(new StructuralError( StructuralError.ErrorType.InvalidType, "Element {i} is of type {valueType}, expected {elementType}", element.Start, element.End )); } } if (context.HasErrors) { return; } for (var i = 0; i < Elements.Count; i++) { context.IL.Emit(OpCodes.Dup); context.IL.Emit(OpCodes.Ldc_I4, i); Elements[i].EmitCode(context, false); if (elementType == BuiltinType.Bool) { context.IL.Emit(OpCodes.Stelem_I4); } else if (elementType == BuiltinType.Int) { context.IL.Emit(OpCodes.Stelem_I8); } else if (elementType == BuiltinType.Float) { context.IL.Emit(OpCodes.Stelem_R8); } else { context.IL.Emit(OpCodes.Stelem, elementType.GeneratedType(context.ModuleContext)); } } }