public JsFunctionDefinitionExpression CompileAutoEventRemover(IEvent @event, EventScriptSemantics impl, string backingFieldName) { try { string valueName = _namer.GetVariableName(@event.RemoveAccessor.Parameters[0].Name, new HashSet <string>(@event.DeclaringTypeDefinition.TypeParameters.Select(p => _namer.GetTypeParameterName(p)))); CreateCompilationContext(null, null, @event.DeclaringTypeDefinition, null); JsExpression target; string[] args; if (@event.IsStatic) { target = _runtimeLibrary.InstantiateType(Utils.SelfParameterize(@event.DeclaringTypeDefinition), _statementCompiler); args = new[] { valueName }; } else if (impl.RemoveMethod.Type == MethodScriptSemantics.ImplType.StaticMethodWithThisAsFirstArgument) { target = JsExpression.Identifier(_namer.ThisAlias); args = new[] { _namer.ThisAlias, valueName }; } else { target = JsExpression.This; args = new[] { valueName }; } var bfAccessor = JsExpression.Member(target, backingFieldName); var combineCall = _statementCompiler.CompileDelegateRemoveCall(@event.RemoveAccessor.Region, bfAccessor, JsExpression.Identifier(valueName)); return(JsExpression.FunctionDefinition(args, JsStatement.Block(JsExpression.Assign(bfAccessor, combineCall)))); } catch (Exception ex) { _errorReporter.Region = @event.Region; _errorReporter.InternalError(ex); return(JsExpression.FunctionDefinition(new string[0], JsStatement.EmptyBlock)); } }
private bool HandleAwaitStatement(JsAwaitStatement stmt, StackEntry location, ImmutableStack <StackEntry> stack, ImmutableStack <Tuple <string, State> > breakStack, ImmutableStack <Tuple <string, State> > continueStack, State currentState, State returnState, IList <JsStatement> currentBlock) { var stateAfter = GetStateAfterStatement(location, stack, currentState.FinallyStack, returnState).Item1; bool createDummyState = false; if (stateAfter.StateValue == returnState.StateValue) { stateAfter = CreateNewStateValue(currentState.FinallyStack); createDummyState = true; // We must never return to our parent state after an await because } currentBlock.Add(new JsSetNextStateStatement(stateAfter.StateValue)); currentBlock.Add(JsExpression.Invocation(JsExpression.Member(stmt.Awaiter, stmt.OnCompletedMethodName), JsExpression.Identifier(_stateMachineMethodName))); if (_needDoFinallyBlocksVariable) { currentBlock.Add(JsExpression.Assign(JsExpression.Identifier(_doFinallyBlocksVariableName), JsExpression.False)); } currentBlock.Add(JsStatement.Return()); if (!stack.IsEmpty || location.Index < location.Block.Statements.Count - 1) { Enqueue(PushFollowing(stack, location), breakStack, continueStack, stateAfter, returnState); } if (createDummyState) { Enqueue(ImmutableStack <StackEntry> .Empty.Push(new StackEntry(JsStatement.Block(JsStatement.BlockMerged(new JsStatement[0])), 0)), breakStack, continueStack, stateAfter, returnState); } return(false); }
private JsExpression GenerateStructCloneMethod(ITypeDefinition type, string typevarName, bool hasCreateInstance) { var stmts = new List <JsStatement>() { JsStatement.Var("r", hasCreateInstance ? (JsExpression)JsExpression.Invocation(JsExpression.Member(JsExpression.Identifier(typevarName), "createInstance")) : JsExpression.New(JsExpression.Identifier(typevarName))) }; var o = JsExpression.Identifier("o"); var r = JsExpression.Identifier("r"); foreach (var f in type.Fields.Where(f => !f.IsStatic)) { var sem = _metadataImporter.GetFieldSemantics(f); if (sem.Type == FieldScriptSemantics.ImplType.Field) { var def = f.ReturnType.GetDefinition(); JsExpression value = JsExpression.Member(o, sem.Name); if (def != null && def.Kind == TypeKind.Struct && _metadataImporter.GetTypeSemantics(def).Type == TypeScriptSemantics.ImplType.MutableValueType) { value = _runtimeLibrary.CloneValueType(value, f.ReturnType, new DefaultRuntimeContext(type, _metadataImporter, _errorReporter, _namer)); } stmts.Add(JsExpression.Assign(JsExpression.Member(r, sem.Name), value)); } } foreach (var p in type.Properties.Where(p => !p.IsStatic)) { var sem = _metadataImporter.GetPropertySemantics(p); if ((sem.Type == PropertyScriptSemantics.ImplType.GetAndSetMethods && MetadataUtils.IsAutoProperty(p) == true) || sem.Type == PropertyScriptSemantics.ImplType.Field) { var def = p.ReturnType.GetDefinition(); var fieldName = sem.Type == PropertyScriptSemantics.ImplType.GetAndSetMethods ? _metadataImporter.GetAutoPropertyBackingFieldName(p) : sem.FieldName; JsExpression value = JsExpression.Member(o, fieldName); if (def != null && def.Kind == TypeKind.Struct && _metadataImporter.GetTypeSemantics(def).Type == TypeScriptSemantics.ImplType.MutableValueType) { value = _runtimeLibrary.CloneValueType(value, p.ReturnType, new DefaultRuntimeContext(type, _metadataImporter, _errorReporter, _namer)); } stmts.Add(JsExpression.Assign(JsExpression.Member(r, fieldName), value)); } } foreach (var e in type.Events.Where(e => !e.IsStatic && MetadataUtils.IsAutoEvent(e) == true)) { var sem = _metadataImporter.GetEventSemantics(e); if (sem.Type == EventScriptSemantics.ImplType.AddAndRemoveMethods) { var def = e.ReturnType.GetDefinition(); var fieldName = _metadataImporter.GetAutoEventBackingFieldName(e); JsExpression value = JsExpression.Member(o, fieldName); if (def != null && def.Kind == TypeKind.Struct && _metadataImporter.GetTypeSemantics(def).Type == TypeScriptSemantics.ImplType.MutableValueType) { value = _runtimeLibrary.CloneValueType(value, e.ReturnType, new DefaultRuntimeContext(type, _metadataImporter, _errorReporter, _namer)); } stmts.Add(JsExpression.Assign(JsExpression.Member(r, fieldName), value)); } } stmts.Add(JsStatement.Return(r)); return(JsExpression.FunctionDefinition(new[] { "o" }, JsStatement.Block(stmts))); }
private JsExpression GenerateStructEqualsMethod(ITypeDefinition type, string typeVariableName) { var o = JsExpression.Identifier("o"); var parts = new List <JsExpression>(); foreach (var f in type.Fields.Where(f => !f.IsStatic)) { var expr = GenerateFieldCompare(f, o); if (expr != null) { parts.Add(expr); } } JsExpression typeCompare = JsExpression.Invocation(JsExpression.Member(_systemScript, "isInstanceOfType"), o, JsExpression.Identifier(typeVariableName)); if (parts.Count == 0) { return(JsExpression.FunctionDefinition(new[] { "o" }, JsStatement.Return(typeCompare))); } else { return(JsExpression.FunctionDefinition(new[] { "o" }, JsStatement.Block( JsStatement.If(JsExpression.LogicalNot(typeCompare), JsStatement.Return(JsExpression.False), null ), JsStatement.Return(parts.Aggregate((old, p) => old == null ? p : JsExpression.LogicalAnd(old, p))) ))); } }
public void ClassWithSameNameAsVariable2() { var someAsm = Common.CreateMockAssembly(); var actual = Process(new JsStatement[] { JsStatement.Function("f", new[] { "x" }, JsStatement.Block( JsStatement.Var("y", JsExpression.Number(0)), JsExpression.FunctionDefinition(new string[0], JsExpression.Binary(ExpressionNodeType.Add, JsExpression.Member(new JsTypeReferenceExpression(Common.CreateMockTypeDefinition("x", someAsm)), "a"), JsExpression.Member(new JsTypeReferenceExpression(Common.CreateMockTypeDefinition("y", someAsm)), "a")) ), JsExpression.FunctionDefinition(new string[0], JsStatement.Block( JsStatement.Var("z", JsExpression.Binary(ExpressionNodeType.Add, JsExpression.Identifier("x"), JsExpression.Identifier("y"))) )) )) }, new[] { Common.CreateMockAssembly(), someAsm }, new MockMetadataImporter { GetTypeSemantics = t => TypeScriptSemantics.NormalType(t.Name) }, namer: new Namer()); AssertCorrect(actual, @"(function() { 'use strict'; var $asm = {}; function f(x1) { var y1 = 0; (function() { x.a + y.a; }); (function() { var z = x1 + y1; }); } })(); "); }
public void RenamedVariableClashWithImplicitGlobal() { var someAsm = Common.CreateMockAssembly(); var actual = Process(new JsStatement[] { JsStatement.Function("f", new[] { "x" }, JsStatement.Block( JsExpression.FunctionDefinition(new string[0], JsExpression.Member(new JsTypeReferenceExpression(Common.CreateMockTypeDefinition("x", someAsm)), "a") ), JsExpression.FunctionDefinition(new string[0], JsStatement.Block( JsExpression.Add(JsExpression.Identifier("x"), JsExpression.Identifier("x1")) )) )) }, new[] { Common.CreateMockAssembly(), someAsm }, new MockMetadataImporter { GetTypeSemantics = t => TypeScriptSemantics.NormalType(t.Name) }, namer: new Namer()); AssertCorrect(actual, @"(function() { 'use strict'; var $asm = {}; function f(x2) { (function() { x.a; }); (function() { x2 + x1; }); } })(); "); }
protected void AssertCorrect(string orig, string expected, MethodType methodType = MethodType.Normal) { int tempIndex = 0, stateIndex = 0, loopLabelIndex = 0; var stmt = JsStatement.EnsureBlock(JavaScriptParser.Parser.ParseStatement(orig, allowCustomKeywords: true)); JsBlockStatement result; if (methodType == MethodType.Iterator) { int finallyHandlerIndex = 0; result = StateMachineRewriter.RewriteIteratorBlock(stmt, e => e.NodeType != ExpressionNodeType.Identifier, () => "$tmp" + (++tempIndex).ToString(CultureInfo.InvariantCulture), () => "$state" + (++stateIndex).ToString(CultureInfo.InvariantCulture), () => string.Format("$loop" + (++loopLabelIndex).ToString(CultureInfo.InvariantCulture)), () => string.Format("$finally" + (++finallyHandlerIndex).ToString(CultureInfo.InvariantCulture)), v => JsExpression.Invocation(JsExpression.Identifier("setCurrent"), v), sm => { var body = new List <JsStatement>(); if (sm.Variables.Count > 0) { body.Add(JsStatement.Var(sm.Variables)); } body.AddRange(sm.FinallyHandlers.Select(h => (JsStatement)JsExpression.Assign(JsExpression.Identifier(h.Item1), h.Item2))); if (sm.Disposer != null) { body.Add(JsExpression.Assign(JsExpression.Identifier("dispose"), JsExpression.FunctionDefinition(new string[0], sm.Disposer))); } body.Add(sm.MainBlock); return(JsStatement.Block(body)); }); } else if (methodType == MethodType.AsyncTask || methodType == MethodType.AsyncVoid) { result = StateMachineRewriter.RewriteAsyncMethod(stmt, e => e.NodeType != ExpressionNodeType.Identifier, () => "$tmp" + (++tempIndex).ToString(CultureInfo.InvariantCulture), () => "$state" + (++stateIndex).ToString(CultureInfo.InvariantCulture), () => string.Format("$loop" + (++loopLabelIndex).ToString(CultureInfo.InvariantCulture)), "$sm", "$doFinally", methodType == MethodType.AsyncTask ? JsStatement.Declaration("$tcs", JsExpression.New(JsExpression.Identifier("TaskCompletionSource"))) : null, expr => { if (methodType != MethodType.AsyncTask) { throw new InvalidOperationException("Should not set result in async void method"); } return(JsExpression.Invocation(JsExpression.Member(JsExpression.Identifier("$tcs"), "setResult"), expr ?? JsExpression.String("<<null>>"))); }, expr => { if (methodType != MethodType.AsyncTask) { throw new InvalidOperationException("Should not set exception in async void method"); } return(JsExpression.Invocation(JsExpression.Member(JsExpression.Identifier("$tcs"), "setException"), expr)); }, () => { if (methodType != MethodType.AsyncTask) { throw new InvalidOperationException("Should not get task async void method"); } return(JsExpression.Invocation(JsExpression.Member(JsExpression.Identifier("$tcs"), "getTask"))); }, (sm, ctx) => JsExpression.Invocation(JsExpression.Identifier("$Bind"), sm, ctx)); } else { result = StateMachineRewriter.RewriteNormalMethod(stmt, e => e.NodeType != ExpressionNodeType.Identifier, () => "$tmp" + (++tempIndex).ToString(CultureInfo.InvariantCulture), () => "$state" + (++stateIndex).ToString(CultureInfo.InvariantCulture), () => string.Format("$loop" + (++loopLabelIndex).ToString(CultureInfo.InvariantCulture))); } var actual = OutputFormatter.Format(result); Assert.That(actual.Replace("\r\n", "\n"), Is.EqualTo(expected.Replace("\r\n", "\n")), "Expected:\n" + expected + "\n\nActual:\n" + actual); }
internal JsBlockStatement Process(JsBlockStatement statement) { _allocateFinallyHandler = null; _makeSetCurrent = null; var result = Process(statement, false, false); var hoistResult = VariableHoistingVisitor.Process(result); return(JsStatement.Block(new[] { JsStatement.Var(new[] { JsStatement.Declaration(_stateVariableName, JsExpression.Number(0)) }.Concat(hoistResult.Item2.Select(v => JsStatement.Declaration(v, null)))) }.Concat(hoistResult.Item1.Statements))); }
private JsBlockStatement ProcessAsyncMethod(JsBlockStatement statement, string stateMachineMethodName, string doFinallyBlocksVariableName, JsVariableDeclaration taskCompletionSource, Func <JsExpression, JsExpression> makeSetResult, Func <JsExpression, JsExpression> makeSetException, Func <JsExpression> getTask, Func <JsExpression, JsExpression, JsExpression> bindToContext) { _stateMachineMethodName = stateMachineMethodName; _doFinallyBlocksVariableName = doFinallyBlocksVariableName; _makeSetResult = taskCompletionSource != null ? makeSetResult : null; _needDoFinallyBlocksVariable = new HasAwaitInsideTryWithFinallyVisitor().Analyze(statement); var result = Process(statement, isIteratorBlock: false, isAsync: true); var hoistResult = VariableHoistingVisitor.Process(result); string catchVariable = _allocateTempVariable(); JsStatement body; if (taskCompletionSource != null && (statement.Statements.Count == 0 || IsNextStatementReachable(statement.Statements[statement.Statements.Count - 1]))) // If we return the task, and if we risk falling out of the original method, we need to add a setResult call. { body = JsStatement.Block(hoistResult.Item1.Statements.Concat(new JsStatement[] { makeSetResult(null) })); } else { body = hoistResult.Item1; } if (taskCompletionSource != null) { body = JsStatement.Try(body, JsStatement.Catch(catchVariable, JsStatement.Block(makeSetException(JsExpression.Identifier(catchVariable)))), null); } IEnumerable <JsVariableDeclaration> declarations = new[] { JsStatement.Declaration(_stateVariableName, JsExpression.Number(0)) }; if (taskCompletionSource != null) { declarations = declarations.Concat(new[] { taskCompletionSource }); } declarations = declarations.Concat(hoistResult.Item2.Select(v => JsStatement.Declaration(v, null))); if (_needDoFinallyBlocksVariable) { body = JsStatement.Block(new[] { JsStatement.Var(_doFinallyBlocksVariableName, JsExpression.True) }.Concat(body is JsBlockStatement ? ((JsBlockStatement)body).Statements : (IEnumerable <JsStatement>) new[] { body })); } var stateMachine = JsExpression.FunctionDefinition(new string[0], body); var boundStateMachine = UsesThisVisitor.Analyze(stateMachine.Body) ? bindToContext(stateMachine, JsExpression.This) : stateMachine; IEnumerable <JsStatement> stmts = new JsStatement[] { JsStatement.Var(declarations), JsStatement.Var(stateMachineMethodName, boundStateMachine), JsExpression.Invocation(JsExpression.Identifier(stateMachineMethodName)) }; if (taskCompletionSource != null) { stmts = stmts.Concat(new[] { JsStatement.Return(getTask()) }); } return(JsStatement.Block(stmts)); }
public static IList <JsStatement> Process(IMetadataImporter metadataImporter, INamer namer, ICompilation compilation, IList <JsStatement> statements) { var usedSymbols = UsedSymbolsGatherer.Analyze(statements); var importer = new ImportVisitor(metadataImporter, namer, compilation.MainAssembly, usedSymbols); var body = statements.Select(s => importer.VisitStatement(s, null)).ToList(); var moduleDependencies = importer._moduleAliases.Concat(MetadataUtils.GetAdditionalDependencies(compilation.MainAssembly)); if (MetadataUtils.IsAsyncModule(compilation.MainAssembly)) { body.InsertRange(0, new[] { JsStatement.UseStrict, JsStatement.Var("exports", JsExpression.ObjectLiteral()) }); body.Add(JsStatement.Return(JsExpression.Identifier("exports"))); var pairs = new[] { new KeyValuePair <string, string>("mscorlib", namer.GetVariableName("_", usedSymbols)) } .Concat(moduleDependencies.OrderBy(x => x.Key)) .ToList(); body = new List <JsStatement> { JsExpression.Invocation( JsExpression.Identifier("define"), JsExpression.ArrayLiteral(pairs.Select(p => JsExpression.String(p.Key))), JsExpression.FunctionDefinition( pairs.Select(p => p.Value), JsStatement.Block(body) ) ) }; } else if (moduleDependencies.Any()) { // If we require any module, we require mscorlib. This should work even if we are a leaf module that doesn't include any other module because our parent script will do the mscorlib require for us. body.InsertRange(0, new[] { JsStatement.UseStrict, JsExpression.Invocation(JsExpression.Identifier("require"), JsExpression.String("mscorlib")) } .Concat(moduleDependencies .OrderBy(x => x.Key).OrderBy(x => x.Key) .Select(x => JsStatement.Var( x.Value, JsExpression.Invocation( JsExpression.Identifier("require"), JsExpression.String(x.Key)))) .ToList())); } else { body.Insert(0, JsStatement.UseStrict); body = new List <JsStatement> { JsExpression.Invocation(JsExpression.FunctionDefinition(new string[0], JsStatement.Block(body))) }; } return(body); }
public static JsBlockStatement GenerateDisposer(string stateVariableName, List <Tuple <int, List <string> > > stateFinallyHandlers) { if (stateFinallyHandlers.Count == 0) { return(null); } return(JsStatement.Block( JsStatement.Try( GenerateBody(stateVariableName, GenerateHandlerTree(stateFinallyHandlers)), null, JsExpression.Assign(JsExpression.Identifier(stateVariableName), JsExpression.Number(-1)) ) )); }
private bool HandleIfStatement(JsIfStatement stmt, StackEntry location, ImmutableStack <StackEntry> stack, ImmutableStack <Tuple <string, State> > breakStack, ImmutableStack <Tuple <string, State> > continueStack, State currentState, State returnState, IList <JsStatement> currentBlock) { var stateAfter = GetStateAfterStatement(location, stack, currentState.FinallyStack, returnState); IList <JsStatement> thenPart, elsePart; if (stmt.Then.Statements.Count == 0) { thenPart = EmptyList <JsStatement> .Instance; } else { thenPart = Handle(ImmutableStack <StackEntry> .Empty.Push(new StackEntry(stmt.Then, 0)), breakStack, continueStack, currentState, stateAfter.Item1, false, false); } if (stmt.Else == null) { elsePart = null; } else if (stmt.Else.Statements.Count == 0) { elsePart = EmptyList <JsStatement> .Instance; } else { elsePart = Handle(ImmutableStack <StackEntry> .Empty.Push(new StackEntry(stmt.Else, 0)), breakStack, continueStack, currentState, stateAfter.Item1, false, false); } currentBlock.Add(JsStatement.If(stmt.Test, JsStatement.Block(thenPart), elsePart != null ? JsStatement.Block(elsePart) : null)); if (thenPart.Count == 0 || elsePart == null || elsePart.Count == 0) { currentBlock.Add(new JsGotoStateStatement(stateAfter.Item1, currentState)); } if (stateAfter.Item2) { Enqueue(PushFollowing(stack, location), breakStack, continueStack, stateAfter.Item1, returnState); return(false); } return(true); }
private bool HandleDoWhileStatement(JsDoWhileStatement stmt, StackEntry location, ImmutableStack <StackEntry> stack, ImmutableStack <Tuple <string, State> > breakStack, ImmutableStack <Tuple <string, State> > continueStack, State currentState, State returnState, IList <JsStatement> currentBlock, bool isFirstStatement) { if (!isFirstStatement) { // We have to create a new block for the statement. var topOfLoopState = CreateNewStateValue(currentState.FinallyStack); Enqueue(stack.Push(location), breakStack, continueStack, topOfLoopState, returnState); currentBlock.Add(new JsGotoStateStatement(topOfLoopState, currentState)); return(false); } else { var beforeConditionState = CreateNewStateValue(currentState.FinallyStack); Tuple <State, bool> afterLoopState; string currentName = GetLabelForState(currentState); if (new ContainsBreakVisitor().Analyze(stmt.Body, currentName)) { afterLoopState = GetStateAfterStatement(location, stack, currentState.FinallyStack, returnState); breakStack = breakStack.Push(Tuple.Create(currentName, afterLoopState.Item1)); } else { afterLoopState = Tuple.Create(returnState, false); } currentBlock.AddRange(Handle(ImmutableStack <StackEntry> .Empty.Push(new StackEntry(stmt.Body, 0)), breakStack, continueStack.Push(Tuple.Create(GetLabelForState(currentState), beforeConditionState)), currentState, beforeConditionState, false, false)); if (afterLoopState.Item2) { Enqueue(PushFollowing(stack, location), breakStack, continueStack, afterLoopState.Item1, returnState); Enqueue(stack.Push(new StackEntry(JsStatement.Block(JsStatement.If(stmt.Condition, new JsGotoStateStatement(currentState, currentState), null)), 0)), breakStack, continueStack, beforeConditionState, afterLoopState.Item1); } else { Enqueue(PushFollowing(stack, location).Push(new StackEntry(JsStatement.Block(JsStatement.If(stmt.Condition, new JsGotoStateStatement(currentState, currentState), null)), 0)), breakStack, continueStack, beforeConditionState, returnState); } return(false); } }
private bool HandleForStatement(JsForStatement stmt, StackEntry location, ImmutableStack <StackEntry> stack, ImmutableStack <Tuple <string, State> > breakStack, ImmutableStack <Tuple <string, State> > continueStack, State currentState, State returnState, IList <JsStatement> currentBlock, bool isFirstStatement) { if (!(isFirstStatement && (stmt.InitStatement is JsEmptyStatement || location.AfterForInitializer))) { // We have to create a new block for the statement. var topOfLoopState = CreateNewStateValue(currentState.FinallyStack); Enqueue(stack.Push(new StackEntry(location.Block, location.Index, true)), breakStack, continueStack, topOfLoopState, returnState); if (!(stmt.InitStatement is JsEmptyStatement)) { currentBlock.Add(stmt.InitStatement); } currentBlock.Add(new JsGotoStateStatement(topOfLoopState, currentState)); return(false); } else { var iteratorState = (stmt.IteratorExpression != null ? CreateNewStateValue(currentState.FinallyStack) : currentState); var afterLoopState = GetStateAfterStatement(location, stack, currentState.FinallyStack, returnState); if (stmt.ConditionExpression != null) { currentBlock.Add(JsStatement.If(JsExpression.LogicalNot(stmt.ConditionExpression), new JsGotoStateStatement(afterLoopState.Item1, currentState), null)); } string currentName = GetLabelForState(currentState); currentBlock.AddRange(Handle(ImmutableStack <StackEntry> .Empty.Push(new StackEntry(stmt.Body, 0)), breakStack.Push(Tuple.Create(currentName, afterLoopState.Item1)), continueStack.Push(Tuple.Create(currentName, iteratorState)), currentState, iteratorState, false, false)); if (stmt.IteratorExpression != null) { Enqueue(ImmutableStack <StackEntry> .Empty.Push(new StackEntry(JsStatement.Block(stmt.IteratorExpression), 0)), breakStack, continueStack, iteratorState, currentState); } if (!stack.IsEmpty || location.Index < location.Block.Statements.Count - 1) { Enqueue(PushFollowing(stack, location), breakStack, continueStack, afterLoopState.Item1, returnState); } return(false); } }
private JsFunctionDefinitionExpression GenerateStructGetHashCodeMethod(ITypeDefinition type) { JsExpression h = JsExpression.Identifier("h"); var stmts = new List <JsStatement>(); foreach (var f in type.Fields.Where(f => !f.IsStatic)) { var expr = GetFieldHashCode(f); if (expr != null) { if (stmts.Count == 0) { stmts.Add(JsStatement.Var("h", expr)); } else { stmts.Add(JsExpression.Assign(h, JsExpression.BitwiseXor(JsExpression.Multiply(h, JsExpression.Number(397)), expr))); } } } switch (stmts.Count) { case 0: stmts.Add(JsStatement.Return(JsExpression.Number(0))); break; case 1: stmts[0] = JsStatement.Return(JsExpression.BitwiseOr(((JsVariableDeclarationStatement)stmts[0]).Declarations[0].Initializer, JsExpression.Number(0))); break; default: stmts.Add(JsStatement.Return(h)); break; } return(JsExpression.FunctionDefinition(EmptyList <string> .Instance, JsStatement.Block(stmts))); }
public static JsExpression ConstructAttribute(IAttribute attr, ITypeDefinition currentType, ICompilation compilation, IMetadataImporter metadataImporter, INamer namer, IRuntimeLibrary runtimeLibrary, IErrorReporter errorReporter) { errorReporter.Region = attr.Region; var initializerStatements = attr.NamedArguments.Select(a => new OperatorResolveResult(a.Key.ReturnType, ExpressionType.Assign, new MemberResolveResult(new InitializedObjectResolveResult(attr.AttributeType), a.Key), a.Value)).ToList <ResolveResult>(); var constructorResult = CompileConstructorInvocation(attr.Constructor, initializerStatements, currentType, null, attr.PositionalArguments, compilation, metadataImporter, namer, runtimeLibrary, errorReporter, null, null); if (constructorResult.AdditionalStatements.Count > 0) { return(JsExpression.Invocation(JsExpression.FunctionDefinition(new string[0], JsStatement.Block(constructorResult.AdditionalStatements.Concat(new[] { JsStatement.Return(constructorResult.Expression) }))))); } else { return(constructorResult.Expression); } }
private bool HandleSwitchStatement(JsSwitchStatement stmt, StackEntry location, ImmutableStack <StackEntry> stack, ImmutableStack <Tuple <string, State> > breakStack, ImmutableStack <Tuple <string, State> > continueStack, State currentState, State returnState, IList <JsStatement> currentBlock) { var stateAfter = GetStateAfterStatement(location, stack, currentState.FinallyStack, returnState); JsExpression expression = stmt.Expression; if (_isExpressionComplexEnoughForATemporaryVariable(expression)) { string newName = _allocateTempVariable(); currentBlock.Add(JsStatement.Var(newName, expression)); expression = JsExpression.Identifier(newName); } var clauses = new List <Tuple <JsExpression, JsBlockStatement> >(); JsStatement defaultClause = null; State? currentFallthroughState = null; for (int i = 0; i < stmt.Sections.Count; i++) { var clause = stmt.Sections[i]; var origBody = new List <JsStatement>(); origBody.AddRange(clause.Body.Statements); State?nextFallthroughState; if (i < stmt.Sections.Count - 1 && (origBody.Count == 0 || IsNextStatementReachable(origBody[origBody.Count - 1]))) { // Fallthrough var nextBody = stmt.Sections[i + 1].Body.Statements; if (nextBody.Count > 0 && nextBody[0] is JsLabelledStatement) { nextFallthroughState = GetOrCreateStateForLabel(((JsLabelledStatement)nextBody[0]).Label, currentState.FinallyStack); } else { nextFallthroughState = CreateNewStateValue(currentState.FinallyStack); } } else { nextFallthroughState = null; } breakStack = breakStack.Push(Tuple.Create(GetLabelForState(currentState), stateAfter.Item1)); IList <JsStatement> body; if (currentFallthroughState != null) { body = new List <JsStatement>(); body.Add(new JsGotoStateStatement(currentFallthroughState.Value, currentState)); Enqueue(ImmutableStack <StackEntry> .Empty.Push(new StackEntry(JsStatement.Block(origBody), 0)), breakStack, continueStack, currentFallthroughState.Value, stateAfter.Item1); } else { body = Handle(ImmutableStack <StackEntry> .Empty.Push(new StackEntry(JsStatement.Block(origBody), 0)), breakStack, continueStack, currentState, nextFallthroughState ?? stateAfter.Item1, false, false); } if (clause.Values.Any(v => v == null)) { defaultClause = JsStatement.Block(body); } else { JsExpression test = clause.Values.Select(v => JsExpression.Same(expression, v)).Aggregate((o, e) => o != null ? JsExpression.LogicalOr(o, e) : e); clauses.Add(Tuple.Create(test, JsStatement.Block(body))); } currentFallthroughState = nextFallthroughState; } clauses.Reverse(); currentBlock.Add(clauses.Where(c => c.Item1 != null).Aggregate(defaultClause, (o, n) => JsStatement.If(n.Item1, n.Item2, o))); currentBlock.Add(new JsGotoStateStatement(stateAfter.Item1, currentState)); if (stateAfter.Item2) { Enqueue(PushFollowing(stack, location), breakStack, continueStack, stateAfter.Item1, returnState); return(false); } return(true); }
private bool HandleTryStatement(JsTryStatement stmt, StackEntry location, ImmutableStack <StackEntry> stack, ImmutableStack <Tuple <string, State> > breakStack, ImmutableStack <Tuple <string, State> > continueStack, State currentState, State returnState, IList <JsStatement> currentBlock, bool isFirstStatement) { if (_isIteratorBlock && (FindInterestingConstructsVisitor.Analyze(stmt.GuardedStatement, InterestingConstruct.YieldReturn) || (stmt.Finally != null && stmt.Catch == null && !currentState.FinallyStack.IsEmpty))) { if (stmt.Catch != null) { throw new InvalidOperationException("Cannot yield return from try with catch"); } string handlerName = _allocateFinallyHandler(); JsBlockStatement handler; if (FindInterestingConstructsVisitor.Analyze(stmt.Finally, InterestingConstruct.Label)) { var inner = ProcessInner(stmt.Finally, breakStack, continueStack, currentState.FinallyStack, currentState.StateValue); handler = JsStatement.Block(new[] { new JsSetNextStateStatement(inner.Item2) }.Concat(inner.Item1)); handler = new FinalizerRewriter(_stateVariableName, _labelStates).Process(handler); } else { handler = stmt.Finally; } _finallyHandlers.Add(Tuple.Create(handlerName, handler)); var stateAfter = GetStateAfterStatement(location, stack, currentState.FinallyStack, returnState); var innerState = CreateNewStateValue(currentState.FinallyStack, handlerName); var stateBeforeFinally = CreateNewStateValue(innerState.FinallyStack); currentBlock.Add(new JsSetNextStateStatement(innerState.StateValue)); currentBlock.AddRange(Handle(ImmutableStack <StackEntry> .Empty.Push(new StackEntry(stmt.GuardedStatement, 0)), breakStack, continueStack, new State(currentState.LoopLabelName, currentState.StateValue, innerState.FinallyStack), stateBeforeFinally, false, false)); Enqueue(ImmutableStack <StackEntry> .Empty.Push(new StackEntry(JsStatement.Block(JsStatement.BlockMerged(new JsStatement[0])), 0)), breakStack, continueStack, stateBeforeFinally, stateAfter.Item1); if (!stack.IsEmpty || location.Index < location.Block.Statements.Count - 1) { Enqueue(PushFollowing(stack, location), breakStack, continueStack, stateAfter.Item1, returnState); } return(false); } else if (_isIteratorBlock && stmt.Finally != null && !currentState.FinallyStack.IsEmpty) { // This is necessary to special-case in order to ensure that the inner finally block is executed before all outer ones. return(HandleTryStatement(JsStatement.Try(JsStatement.Try(stmt.GuardedStatement, stmt.Catch, null), null, stmt.Finally), location, stack, breakStack, continueStack, currentState, returnState, currentBlock, isFirstStatement)); } else { var rewriter = new NestedStatementFixer(breakStack, continueStack, currentState, _exitState.Value, _makeSetResult); JsBlockStatement guarded; var guardedConstructs = FindInterestingConstructsVisitor.Analyze(stmt.GuardedStatement); if ((guardedConstructs & (InterestingConstruct.Label | InterestingConstruct.Await)) != InterestingConstruct.None) { if (!isFirstStatement) { var sv = CreateNewStateValue(currentState.FinallyStack); Enqueue(stack.Push(location), breakStack, continueStack, sv, returnState); currentBlock.Add(new JsGotoStateStatement(sv, currentState)); return(false); } var inner = ProcessInner(stmt.GuardedStatement, breakStack, continueStack, currentState.FinallyStack, currentState.StateValue); guarded = JsStatement.Block(inner.Item1); currentBlock.Add(new JsSetNextStateStatement(inner.Item2)); } else { guarded = rewriter.Process(stmt.GuardedStatement); } JsCatchClause @catch; if (stmt.Catch != null) { if (FindInterestingConstructsVisitor.Analyze(stmt.Catch.Body, InterestingConstruct.Label)) { var inner = ProcessInner(stmt.Catch.Body, breakStack, continueStack, currentState.FinallyStack, null); @catch = JsStatement.Catch(stmt.Catch.Identifier, JsStatement.Block(new[] { new JsSetNextStateStatement(inner.Item2) }.Concat(inner.Item1))); } else { var body = rewriter.Process(stmt.Catch.Body); @catch = ReferenceEquals(body, stmt.Catch.Body) ? stmt.Catch : JsStatement.Catch(stmt.Catch.Identifier, body); } } else { @catch = null; } JsBlockStatement @finally; if (stmt.Finally != null) { if (FindInterestingConstructsVisitor.Analyze(stmt.Finally, InterestingConstruct.Label)) { var inner = ProcessInner(stmt.Finally, breakStack, continueStack, currentState.FinallyStack, null); @finally = JsStatement.Block(new[] { new JsSetNextStateStatement(inner.Item2) }.Concat(inner.Item1)); } else { @finally = rewriter.Process(stmt.Finally); } if ((guardedConstructs & InterestingConstruct.Await) != InterestingConstruct.None) { // Wrap the finally block inside an 'if (doFinallyBlocks) {}' @finally = JsStatement.Block(JsStatement.If(JsExpression.Identifier(_doFinallyBlocksVariableName), @finally, null)); } } else { @finally = null; } if (currentBlock.Count > 0 && _childStates.ContainsKey(currentState.StateValue)) { var newBlock = JsStatement.If(JsExpression.Same(JsExpression.Identifier(_stateVariableName), JsExpression.Number(currentState.StateValue)), JsStatement.Block(currentBlock), null); currentBlock.Clear(); currentBlock.Add(newBlock); } currentBlock.Add(JsStatement.Try(guarded, @catch, @finally)); return(true); } }
public override JsStatement VisitFunctionStatement(JsFunctionStatement statement, object data) { var body = (JsBlockStatement)VisitStatement(statement.Body, null); return(JsStatement.Function(statement.Name, statement.ParameterNames, JsStatement.Block(MakePrefix(statement).Concat(body.Statements)))); }
private JsBlockStatement Process(JsBlockStatement statement, bool isIteratorBlock, bool isAsync) { _stateVariableName = _allocateStateVariable(); _nextStateIndex = 0; _isIteratorBlock = isIteratorBlock; _isAsync = isAsync; _processedStates = new HashSet <State>(); _labelStates = new Dictionary <string, State>(); _finallyHandlers = new List <Tuple <string, JsBlockStatement> >(); _allStates = new List <State>(); _remainingBlocks = new Queue <RemainingBlock>(); _exitState = null; _childStates = new Dictionary <int, List <int> >(); var body = ProcessInner(statement, ImmutableStack <Tuple <string, State> > .Empty, ImmutableStack <Tuple <string, State> > .Empty, ImmutableStack <Tuple <int, string> > .Empty, null).Item1; if (_isIteratorBlock) { body.Add(JsStatement.Return(JsExpression.False)); } var resultBody = new FinalizerRewriter(_stateVariableName, _labelStates).Process(JsStatement.Block(body)); return(resultBody); }
public JsFunctionDefinitionExpression CompileConstructor(ConstructorDeclaration ctor, IMethod constructor, List <JsStatement> instanceInitStatements, ConstructorScriptSemantics impl) { var region = _errorReporter.Region = ctor != null?ctor.GetRegion() : constructor.DeclaringTypeDefinition.Region; try { CreateCompilationContext(ctor, constructor, constructor.DeclaringTypeDefinition, (impl.Type == ConstructorScriptSemantics.ImplType.StaticMethod ? _namer.ThisAlias : null)); IList <JsStatement> body = new List <JsStatement>(); body.AddRange(PrepareParameters(constructor.Parameters, variables, expandParams: impl.ExpandParams, staticMethodWithThisAsFirstArgument: false)); if (impl.Type == ConstructorScriptSemantics.ImplType.StaticMethod) { if (ctor != null && !ctor.Initializer.IsNull) { body.AddRange(_statementCompiler.CompileConstructorInitializer(ctor.Initializer, true)); } else { body.AddRange(_statementCompiler.CompileImplicitBaseConstructorCall(constructor.DeclaringTypeDefinition, true)); } } if (ctor == null || ctor.Initializer.IsNull || ctor.Initializer.ConstructorInitializerType != ConstructorInitializerType.This) { if (impl.Type == ConstructorScriptSemantics.ImplType.StaticMethod) { // The compiler one step up has created the statements as "this.a = b;", but we need to replace that with "$this.a = b;" (or whatever name the this alias has). var replacer = new ThisReplacer(JsExpression.Identifier(_namer.ThisAlias)); instanceInitStatements = instanceInitStatements.Select(s => replacer.VisitStatement(s, null)).ToList(); } body.AddRange(instanceInitStatements); // Don't initialize fields when we are chaining, but do it when we 1) compile the default constructor, 2) don't have an initializer, or 3) when the initializer is not this(...). } if (impl.Type != ConstructorScriptSemantics.ImplType.StaticMethod) { if (ctor != null && !ctor.Initializer.IsNull) { body.AddRange(_statementCompiler.CompileConstructorInitializer(ctor.Initializer, false)); } else { body.AddRange(_statementCompiler.CompileImplicitBaseConstructorCall(constructor.DeclaringTypeDefinition, false)); } } if (ctor != null) { body.AddRange(_statementCompiler.Compile(ctor.Body).Statements); } if (impl.Type == ConstructorScriptSemantics.ImplType.StaticMethod) { if (body.Count == 0 || !(body[body.Count - 1] is JsReturnStatement)) { body.Add(JsStatement.Return()); } body = StaticMethodConstructorReturnPatcher.Process(body, _namer.ThisAlias).AsReadOnly(); } var compiled = JsExpression.FunctionDefinition(constructor.Parameters.Where((p, i) => i != constructor.Parameters.Count - 1 || !impl.ExpandParams).Select(p => variables[p].Name), JsStatement.Block(body)); return(_statementCompiler.StateMachineRewriteNormalMethod(compiled)); } catch (Exception ex) { _errorReporter.Region = region; _errorReporter.InternalError(ex); return(JsExpression.FunctionDefinition(new string[0], JsStatement.EmptyBlock)); } }
public JsStatement Process(JsBlockStatement stmt) { stmt = (JsBlockStatement)VisitStatement(stmt, null); return(JsStatement.Block(MakePrefix(JsDeclarationScope.Root).Concat(stmt.Statements))); }
private static JsStatement GenerateBody(string stateVariableName, List <Node> nodes) { return(JsStatement.Switch(JsExpression.Identifier(stateVariableName), nodes.Select(n => JsStatement.SwitchSection(n.StateValues.Select(v => JsExpression.Number(v)), JsStatement.Try( n.Children.Count > 0 ? (JsStatement)JsStatement.Block(GenerateBody(stateVariableName, n.Children), JsStatement.Break()) : JsStatement.Break(), null, JsExpression.Invocation(JsExpression.Member(JsExpression.Identifier(n.HandlerName), "call"), JsExpression.This)))))); }
public void CanAnalyzeStatements() { var asm = Common.CreateMockAssembly(); var t1 = Common.CreateMockTypeDefinition("Type", asm); var t2 = Common.CreateMockTypeDefinition("Type", asm); var t3 = Common.CreateMockTypeDefinition("Type", asm); var ast = new JsStatement[] { JsStatement.If(JsExpression.Member(new JsTypeReferenceExpression(t1), "X"), JsStatement.Block( JsExpression.Add(new JsTypeReferenceExpression(t2), new JsTypeReferenceExpression(t3)) ), null), JsExpression.Add(JsExpression.Number(1), new JsTypeReferenceExpression(t1)), }; var refs = TypeReferenceFinder.Analyze(ast); Assert.That(refs, Has.Count.EqualTo(3)); Assert.That(refs.Contains(t1)); Assert.That(refs.Contains(t2)); Assert.That(refs.Contains(t3)); }
private static JsExpression ConstructConstructorInfo(IMethod constructor, ICompilation compilation, IMetadataImporter metadataImporter, INamer namer, IRuntimeLibrary runtimeLibrary, IErrorReporter errorReporter, Func <IType, JsExpression> instantiateType, bool includeDeclaringType) { var properties = GetCommonMemberInfoProperties(constructor, compilation, metadataImporter, namer, runtimeLibrary, errorReporter, instantiateType, includeDeclaringType); var sem = metadataImporter.GetConstructorSemantics(constructor); if (sem.Type == ConstructorScriptSemantics.ImplType.NotUsableFromScript) { errorReporter.Message(Messages._7200, constructor.FullName); return(JsExpression.Null); } properties.Add(new JsObjectLiteralProperty("type", JsExpression.Number((int)MemberTypes.Constructor))); properties.Add(new JsObjectLiteralProperty("params", JsExpression.ArrayLiteral(constructor.Parameters.Select(p => instantiateType(p.Type))))); if (sem.Type == ConstructorScriptSemantics.ImplType.NamedConstructor || sem.Type == ConstructorScriptSemantics.ImplType.StaticMethod) { properties.Add(new JsObjectLiteralProperty("sname", JsExpression.String(sem.Name))); } if (sem.Type == ConstructorScriptSemantics.ImplType.StaticMethod) { properties.Add(new JsObjectLiteralProperty("sm", JsExpression.True)); } if ((sem.Type == ConstructorScriptSemantics.ImplType.UnnamedConstructor || sem.Type == ConstructorScriptSemantics.ImplType.NamedConstructor || sem.Type == ConstructorScriptSemantics.ImplType.StaticMethod) && sem.ExpandParams) { properties.Add(new JsObjectLiteralProperty("exp", JsExpression.True)); } if (sem.Type == ConstructorScriptSemantics.ImplType.Json || sem.Type == ConstructorScriptSemantics.ImplType.InlineCode) { var usedNames = new HashSet <string>(); var parameters = new List <IVariable>(); var variables = new Dictionary <IVariable, VariableData>(); IList <ResolveResult> constructorParameters = null; IList <ResolveResult> initializerStatements = null; if (sem.Type == ConstructorScriptSemantics.ImplType.Json && constructor.DeclaringType.Kind == TypeKind.Anonymous) { initializerStatements = new List <ResolveResult>(); foreach (var p in constructor.DeclaringType.GetProperties()) { string paramName = MakeCamelCase(p.Name); string name = namer.GetVariableName(paramName, usedNames); usedNames.Add(name); var variable = new SimpleVariable(p.ReturnType, paramName, DomRegion.Empty); parameters.Add(variable); variables.Add(variable, new VariableData(name, null, false)); initializerStatements.Add(new OperatorResolveResult(p.ReturnType, ExpressionType.Assign, new MemberResolveResult(new InitializedObjectResolveResult(constructor.DeclaringType), p), new LocalResolveResult(variable))); } } else { constructorParameters = new List <ResolveResult>(); foreach (var p in constructor.Parameters) { string name = namer.GetVariableName(p.Name, usedNames); usedNames.Add(name); var variable = new SimpleVariable(p.Type, p.Name, DomRegion.Empty); parameters.Add(variable); variables.Add(variable, new VariableData(name, null, false)); constructorParameters.Add(new LocalResolveResult(variable)); } } var compileResult = CompileConstructorInvocation(constructor, initializerStatements, constructor.DeclaringTypeDefinition, constructor, constructorParameters, compilation, metadataImporter, namer, runtimeLibrary, errorReporter, variables, usedNames); var definition = JsExpression.FunctionDefinition(parameters.Select(p => variables[p].Name), JsStatement.Block(compileResult.AdditionalStatements.Concat(new[] { JsStatement.Return(compileResult.Expression) }))); properties.Add(new JsObjectLiteralProperty("def", definition)); } return(JsExpression.ObjectLiteral(properties)); }
private Tuple <List <JsStatement>, int> ProcessInner(JsBlockStatement statement, ImmutableStack <Tuple <string, State> > breakStack, ImmutableStack <Tuple <string, State> > continueStack, ImmutableStack <Tuple <int, string> > finallyStack, int?parentState) { var oldLoopLabel = _currentLoopLabel; var oldRemainingBlocks = _remainingBlocks; try { _currentLoopLabel = _allocateLoopLabel(); _remainingBlocks = new Queue <RemainingBlock>(); _remainingBlocks.Enqueue(new RemainingBlock(ImmutableStack <StackEntry> .Empty.Push(new StackEntry(statement, 0)), breakStack, continueStack, CreateNewStateValue(finallyStack), new State(_currentLoopLabel, -1, finallyStack))); if (_exitState == null) { _exitState = new State(_currentLoopLabel, -1, ImmutableStack <Tuple <int, string> > .Empty); } var sections = new List <Section>(); int iterationCount = 0; while (_remainingBlocks.Count > 0) { var current = _remainingBlocks.Dequeue(); var list = Handle(current.Stack, current.BreakStack, current.ContinueStack, current.StateValue, current.ReturnState, _isIteratorBlock || _isAsync, true); // Merge all top-level blocks that should be merged with their parents. list = list.SelectMany(stmt => (stmt is JsBlockStatement && ((JsBlockStatement)stmt).MergeWithParent) ? ((JsBlockStatement)stmt).Statements : (IList <JsStatement>) new[] { stmt }).ToList(); sections.Add(new Section(current.StateValue, list)); if (iterationCount++ > 100000) { throw new Exception("Infinite loop when rewriting method to a state machine"); } } if (parentState != null && _isAsync) { List <int> childStates; if (!_childStates.TryGetValue(parentState.Value, out childStates)) { _childStates[parentState.Value] = childStates = new List <int>(); } childStates.AddRange(sections.Select(s => s.State.StateValue)); } var body = new List <JsStatement> { JsStatement.Label(_currentLoopLabel, JsStatement.For(JsStatement.Empty, null, null, JsStatement.Switch(JsExpression.Identifier(_stateVariableName), sections.Select(b => JsStatement.SwitchSection( GetAllContainedStateValues(b.State.StateValue).OrderBy(v => v).Select(v => JsExpression.Number(v)), JsStatement.Block(b.Statements))) .Concat(new[] { JsStatement.SwitchSection(new JsExpression[] { null }, JsStatement.Break(_currentLoopLabel)) })))) }; return(Tuple.Create(body, sections[0].State.StateValue)); } finally { _currentLoopLabel = oldLoopLabel; _remainingBlocks = oldRemainingBlocks; } }
private static JsExpression ConstructMemberInfo(IMember m, ICompilation compilation, IMetadataImporter metadataImporter, INamer namer, IRuntimeLibrary runtimeLibrary, IErrorReporter errorReporter, Func <IType, JsExpression> instantiateType, bool includeDeclaringType, MethodScriptSemantics semanticsIfAccessor) { if (m is IMethod && ((IMethod)m).IsConstructor) { return(ConstructConstructorInfo((IMethod)m, compilation, metadataImporter, namer, runtimeLibrary, errorReporter, instantiateType, includeDeclaringType)); } var properties = GetCommonMemberInfoProperties(m, compilation, metadataImporter, namer, runtimeLibrary, errorReporter, instantiateType, includeDeclaringType); if (m.IsStatic) { properties.Add(new JsObjectLiteralProperty("isStatic", JsExpression.True)); } if (m is IMethod) { var method = (IMethod)m; var sem = semanticsIfAccessor ?? metadataImporter.GetMethodSemantics(method); if (sem.Type != MethodScriptSemantics.ImplType.NormalMethod && sem.Type != MethodScriptSemantics.ImplType.StaticMethodWithThisAsFirstArgument && sem.Type != MethodScriptSemantics.ImplType.InlineCode) { errorReporter.Message(Messages._7201, m.FullName, "method"); return(JsExpression.Null); } if ((sem.Type == MethodScriptSemantics.ImplType.NormalMethod || sem.Type == MethodScriptSemantics.ImplType.StaticMethodWithThisAsFirstArgument) && sem.ExpandParams) { properties.Add(new JsObjectLiteralProperty("exp", JsExpression.True)); } properties.Add(new JsObjectLiteralProperty("type", JsExpression.Number((int)MemberTypes.Method))); if (sem.Type == MethodScriptSemantics.ImplType.InlineCode) { var usedNames = new HashSet <string>(); var parameters = new List <IVariable>(); var variables = new Dictionary <IVariable, VariableData>(); var arguments = new List <ResolveResult>(); foreach (var p in method.Parameters) { string name = namer.GetVariableName(p.Name, usedNames); usedNames.Add(name); var variable = new SimpleVariable(p.Type, p.Name, DomRegion.Empty); parameters.Add(variable); variables.Add(variable, new VariableData(name, null, false)); arguments.Add(new LocalResolveResult(variable)); } var tokens = InlineCodeMethodCompiler.Tokenize(method, sem.LiteralCode, _ => {}); var compileResult = Compile(CreateMethodInvocationResolveResult(method, method.IsStatic ? (ResolveResult) new TypeResolveResult(method.DeclaringType) : new ThisResolveResult(method.DeclaringType), arguments), method.DeclaringTypeDefinition, method, compilation, metadataImporter, namer, runtimeLibrary, errorReporter, true, variables, usedNames); var definition = JsExpression.FunctionDefinition(parameters.Select(p => variables[p].Name), JsStatement.Block(compileResult.AdditionalStatements.Concat(new[] { JsStatement.Return(compileResult.Expression) }))); if (tokens.Any(t => t.Type == InlineCodeToken.TokenType.TypeParameter && t.OwnerType == SymbolKind.Method)) { definition = JsExpression.FunctionDefinition(method.TypeParameters.Select(namer.GetTypeParameterName), JsStatement.Return(definition)); properties.Add(new JsObjectLiteralProperty("tpcount", JsExpression.Number(method.TypeParameters.Count))); } properties.Add(new JsObjectLiteralProperty("def", definition)); } else { if (IsJsGeneric(method, metadataImporter)) { properties.Add(new JsObjectLiteralProperty("tpcount", JsExpression.Number(method.TypeParameters.Count))); } if (sem.Type == MethodScriptSemantics.ImplType.StaticMethodWithThisAsFirstArgument) { properties.Add(new JsObjectLiteralProperty("sm", JsExpression.True)); } properties.Add(new JsObjectLiteralProperty("sname", JsExpression.String(sem.Name))); } properties.Add(new JsObjectLiteralProperty("returnType", instantiateType(method.ReturnType))); properties.Add(new JsObjectLiteralProperty("params", JsExpression.ArrayLiteral(method.Parameters.Select(p => instantiateType(p.Type))))); } else if (m is IField) { var field = (IField)m; var sem = metadataImporter.GetFieldSemantics(field); if (sem.Type != FieldScriptSemantics.ImplType.Field) { errorReporter.Message(Messages._7201, m.FullName, "field"); return(JsExpression.Null); } properties.Add(new JsObjectLiteralProperty("type", JsExpression.Number((int)MemberTypes.Field))); properties.Add(new JsObjectLiteralProperty("returnType", instantiateType(field.ReturnType))); properties.Add(new JsObjectLiteralProperty("sname", JsExpression.String(sem.Name))); } else if (m is IProperty) { var prop = (IProperty)m; var sem = metadataImporter.GetPropertySemantics(prop); properties.Add(new JsObjectLiteralProperty("type", JsExpression.Number((int)MemberTypes.Property))); properties.Add(new JsObjectLiteralProperty("returnType", instantiateType(prop.ReturnType))); if (prop.Parameters.Count > 0) { properties.Add(new JsObjectLiteralProperty("params", JsExpression.ArrayLiteral(prop.Parameters.Select(p => instantiateType(p.Type))))); } switch (sem.Type) { case PropertyScriptSemantics.ImplType.GetAndSetMethods: if (sem.GetMethod != null && sem.GetMethod.Type != MethodScriptSemantics.ImplType.NormalMethod && sem.GetMethod.Type != MethodScriptSemantics.ImplType.StaticMethodWithThisAsFirstArgument && sem.GetMethod.Type != MethodScriptSemantics.ImplType.InlineCode) { errorReporter.Message(Messages._7202, m.FullName, "property", "getter"); return(JsExpression.Null); } if (sem.SetMethod != null && sem.SetMethod.Type != MethodScriptSemantics.ImplType.NormalMethod && sem.SetMethod.Type != MethodScriptSemantics.ImplType.StaticMethodWithThisAsFirstArgument && sem.SetMethod.Type != MethodScriptSemantics.ImplType.InlineCode) { errorReporter.Message(Messages._7202, m.FullName, "property", "setter"); return(JsExpression.Null); } if (sem.GetMethod != null) { properties.Add(new JsObjectLiteralProperty("getter", ConstructMemberInfo(prop.Getter, compilation, metadataImporter, namer, runtimeLibrary, errorReporter, instantiateType, includeDeclaringType, sem.GetMethod))); } if (sem.SetMethod != null) { properties.Add(new JsObjectLiteralProperty("setter", ConstructMemberInfo(prop.Setter, compilation, metadataImporter, namer, runtimeLibrary, errorReporter, instantiateType, includeDeclaringType, sem.SetMethod))); } break; case PropertyScriptSemantics.ImplType.Field: if (prop.CanGet) { properties.Add(new JsObjectLiteralProperty("getter", ConstructFieldPropertyAccessor(prop.Getter, compilation, metadataImporter, namer, runtimeLibrary, errorReporter, sem.FieldName, instantiateType, isGetter: true, includeDeclaringType: includeDeclaringType))); } if (prop.CanSet) { properties.Add(new JsObjectLiteralProperty("setter", ConstructFieldPropertyAccessor(prop.Setter, compilation, metadataImporter, namer, runtimeLibrary, errorReporter, sem.FieldName, instantiateType, isGetter: false, includeDeclaringType: includeDeclaringType))); } properties.Add(new JsObjectLiteralProperty("fname", JsExpression.String(sem.FieldName))); break; default: errorReporter.Message(Messages._7201, m.FullName, "property"); return(JsExpression.Null); } } else if (m is IEvent) { var evt = (IEvent)m; var sem = metadataImporter.GetEventSemantics(evt); if (sem.Type != EventScriptSemantics.ImplType.AddAndRemoveMethods) { errorReporter.Message(Messages._7201, m.FullName, "event"); return(JsExpression.Null); } if (sem.AddMethod.Type != MethodScriptSemantics.ImplType.NormalMethod && sem.AddMethod.Type != MethodScriptSemantics.ImplType.StaticMethodWithThisAsFirstArgument && sem.AddMethod.Type != MethodScriptSemantics.ImplType.InlineCode) { errorReporter.Message(Messages._7202, m.FullName, "event", "add accessor"); return(JsExpression.Null); } if (sem.RemoveMethod.Type != MethodScriptSemantics.ImplType.NormalMethod && sem.RemoveMethod.Type != MethodScriptSemantics.ImplType.StaticMethodWithThisAsFirstArgument && sem.RemoveMethod.Type != MethodScriptSemantics.ImplType.InlineCode) { errorReporter.Message(Messages._7202, m.FullName, "event", "remove accessor"); return(JsExpression.Null); } properties.Add(new JsObjectLiteralProperty("type", JsExpression.Number((int)MemberTypes.Event))); properties.Add(new JsObjectLiteralProperty("adder", ConstructMemberInfo(evt.AddAccessor, compilation, metadataImporter, namer, runtimeLibrary, errorReporter, instantiateType, includeDeclaringType, sem.AddMethod))); properties.Add(new JsObjectLiteralProperty("remover", ConstructMemberInfo(evt.RemoveAccessor, compilation, metadataImporter, namer, runtimeLibrary, errorReporter, instantiateType, includeDeclaringType, sem.RemoveMethod))); } else { throw new ArgumentException("Invalid member " + m); } return(JsExpression.ObjectLiteral(properties)); }
public JsStatement Process(JsBlockStatement stmt) { stmt = (JsBlockStatement)VisitStatement(stmt, null); return(JsStatement.Block(MakePrefix(new Minifier.Function()).Concat(stmt.Statements))); }
private JsType ConvertType(JsClass type) { if (type.InstanceMethods.Any(m => m.Name == "runTests")) { _errorReporter.Region = type.CSharpTypeDefinition.Region; _errorReporter.Message(MessageSeverity.Error, 7019, string.Format("The type {0} cannot define a method named 'runTests' because it has a [TestFixtureAttribute].", type.CSharpTypeDefinition.FullName)); return(type); } var instanceMethods = new List <JsMethod>(); var tests = new List <Tuple <string, string, bool, int?, JsFunctionDefinitionExpression> >(); foreach (var method in type.InstanceMethods) { var testAttr = _attributeStore.AttributesFor(method.CSharpMember).GetAttribute <TestAttribute>(); if (testAttr != null) { if (!method.CSharpMember.IsPublic || !method.CSharpMember.ReturnType.IsKnownType(KnownTypeCode.Void) || ((IMethod)method.CSharpMember).Parameters.Count > 0 || ((IMethod)method.CSharpMember).TypeParameters.Count > 0) { _errorReporter.Region = method.CSharpMember.Region; _errorReporter.Message(MessageSeverity.Error, 7020, string.Format("Method {0}: Methods decorated with a [TestAttribute] must be public, non-generic, parameterless instance methods that return void.", method.CSharpMember.FullName)); } tests.Add(Tuple.Create(testAttr.Description ?? method.CSharpMember.Name, testAttr.Category, testAttr.IsAsync, testAttr.ExpectedAssertionCount >= 0 ? (int?)testAttr.ExpectedAssertionCount : null, method.Definition)); } else { instanceMethods.Add(method); } } var testInvocations = new List <JsExpression>(); foreach (var category in tests.GroupBy(t => t.Item2).Select(g => new { Category = g.Key, Tests = g.Select(x => new { Description = x.Item1, IsAsync = x.Item3, ExpectedAssertionCount = x.Item4, Function = x.Item5 }) }).OrderBy(x => x.Category)) { if (category.Category != null) { testInvocations.Add(JsExpression.Invocation(JsExpression.Member(JsExpression.Identifier("QUnit"), "module"), JsExpression.String(category.Category))); } testInvocations.AddRange(category.Tests.Select(t => JsExpression.Invocation(JsExpression.Identifier(t.IsAsync ? "asyncTest" : "test"), t.ExpectedAssertionCount != null ? new JsExpression[] { JsExpression.String(t.Description), JsExpression.Number(t.ExpectedAssertionCount.Value), _runtimeLibrary.Bind(t.Function, JsExpression.This, this) } : new JsExpression[] { JsExpression.String(t.Description), _runtimeLibrary.Bind(t.Function, JsExpression.This, this) }))); } instanceMethods.Add(new JsMethod(null, "runTests", null, JsExpression.FunctionDefinition(new string[0], JsStatement.Block(testInvocations.Select(t => (JsStatement)t))))); var result = type.Clone(); result.InstanceMethods.Clear(); foreach (var m in instanceMethods) { result.InstanceMethods.Add(m); } return(result); }
public override JsExpression VisitFunctionDefinitionExpression(JsFunctionDefinitionExpression expression, object data) { var body = (JsBlockStatement)VisitStatement(expression.Body, null); return(JsExpression.FunctionDefinition(expression.ParameterNames, JsStatement.Block(MakePrefix(expression).Concat(body.Statements)), expression.Name)); }