// generateNominalUnwrapping generates the expression source for the unwrapping of a nominal instance of a base type. func (eg *expressionGenerator) generateNominalUnwrapping(nominalUnwrapping *codedom.NominalUnwrappingNode, context generationContext) esbuilder.ExpressionBuilder { // If this is an unwrap is of a wrap, then try to either cancel both operations (if we can), // or simplify the unwrapping operation. childExpression := nominalUnwrapping.ChildExpression if wrapping, ok := nominalUnwrapping.ChildExpression.(*codedom.NominalWrappingNode); ok { // If the value that was being wrapped is, itself, nominal or structural, then we still // need to unwrap it, but we can fast-path by ignoring the wrap and directly unwrapping // the nominal/struct. Otherwise, we can simply just collapse the unwrap+wrap into a NOOP. if !wrapping.IsLiteralWrap && wrapping.ChildExpressionType.IsNominalOrStruct() { childExpression = wrapping.ChildExpression } else { // Otherwise, we can just collapse both operations into nothing. return eg.generateExpression(wrapping.ChildExpression, context) } } // If the child expression being unwrapped is non-nullable and we know it is boxed, then // we can use a fast-path unbox call without the extra checks. if !nominalUnwrapping.ChildExpressionType.NullValueAllowed() && nominalUnwrapping.ChildExpressionType.IsNominalOrStruct() { access := codedom.NativeAccess( childExpression, codedom.BoxedDataProperty, nominalUnwrapping.BasisNode()) return eg.generateExpression(access, context) } call := codedom.RuntimeFunctionCall( codedom.UnboxFunction, []codedom.Expression{ childExpression, }, nominalUnwrapping.BasisNode()) return eg.generateExpression(call, context) }
// generateMemberAssignment generates the expression source for a member assignment. func (eg *expressionGenerator) generateMemberAssignment(memberAssign *codedom.MemberAssignmentNode, context generationContext) esbuilder.ExpressionBuilder { basisNode := memberAssign.BasisNode() // If the target member is an operator, then we need to invoke it as a function call, with the first // argument being the argument to the child call, and the second argument being the assigned child // expression. if memberAssign.Target.IsOperator() { childCall := memberAssign.NameExpression.(*codedom.MemberCallNode) memberRef := childCall.ChildExpression.(*codedom.MemberReferenceNode) // If this is a native operator, change it into a native indexing and assignment. if memberAssign.Target.IsNative() { nativeAssign := codedom.NativeAssign( codedom.NativeIndexing(memberRef.ChildExpression, childCall.Arguments[0], basisNode), memberAssign.Value, basisNode) return eg.generateExpression(nativeAssign, context) } else { memberCall := codedom.MemberCall( codedom.NativeAccess(memberRef.ChildExpression, eg.pather.GetMemberName(memberAssign.Target), memberRef.BasisNode()), memberAssign.Target, []codedom.Expression{childCall.Arguments[0], memberAssign.Value}, basisNode) return eg.generateExpression(memberCall, context) } } // If the target member is implicitly called, then this is a property that needs to be assigned via a call. if memberAssign.Target.IsImplicitlyCalled() { memberRef := memberAssign.NameExpression.(*codedom.MemberReferenceNode) memberCall := codedom.MemberCall( codedom.NativeAccess(memberRef.ChildExpression, eg.pather.GetSetterName(memberRef.Member), memberRef.BasisNode()), memberAssign.Target, []codedom.Expression{memberAssign.Value}, basisNode) return eg.generateExpression(memberCall, context) } value := eg.generateExpression(memberAssign.Value, context) targetExpr := eg.generateExpression(memberAssign.NameExpression, context) return esbuilder.Assignment(targetExpr, value) }
// generateMemberReference generates the expression for a reference to a module or type member. func (eg *expressionGenerator) generateMemberReference(memberReference *codedom.MemberReferenceNode, context generationContext) esbuilder.ExpressionBuilder { // If the target member is implicitly called, then this is a property that needs to be accessed via a call. if memberReference.Member.IsImplicitlyCalled() { basisNode := memberReference.BasisNode() memberCall := codedom.MemberCall( codedom.NativeAccess(memberReference.ChildExpression, memberReference.Member.Name(), basisNode), memberReference.Member, []codedom.Expression{}, basisNode) return eg.generateExpression(memberCall, context) } // This handles the native new case for WebIDL. We should probably handle this directly. if memberReference.Member.IsStatic() && !memberReference.Member.IsPromising() { return eg.generateExpression(codedom.StaticMemberReference(memberReference.Member, eg.scopegraph.TypeGraph().AnyTypeReference(), memberReference.BasisNode()), context) } childExpr := eg.generateExpression(memberReference.ChildExpression, context) return childExpr.Member(eg.pather.GetMemberName(memberReference.Member)) }
// buildLoopStatement builds the CodeDOM for a loop statement. func (db *domBuilder) buildLoopStatement(node compilergraph.GraphNode) (codedom.Statement, codedom.Statement) { startStatement := codedom.EmptyStatement(node) startStatement.MarkReferenceable() finalStatement := codedom.EmptyStatement(node) // Save initial continue and break statements for the loop. db.continueStatementMap[node.NodeId] = startStatement db.breakStatementMap[node.NodeId] = finalStatement // A loop statement is buildd as a start statement which conditionally jumps to either the loop body // (on true) or, on false, jumps to a final state after the loop. if loopExpr, hasLoopExpr := db.tryGetExpression(node, parser.NodeLoopStatementExpression); hasLoopExpr { // Check for a named value under the loop. If found, this is a loop over a stream or streamable. namedValue, hasNamedValue := node.TryGetNode(parser.NodeStatementNamedValue) if hasNamedValue { namedValueName := namedValue.Get(parser.NodeNamedValueName) resultVarName := db.generateScopeVarName(node) namedValueScope, _ := db.scopegraph.GetScope(namedValue) // Create the stream variable. streamVarName := db.generateScopeVarName(node) var streamVariable = codedom.VarDefinitionWithInit(streamVarName, loopExpr, node) // If the expression is Streamable, first call .Stream() on the expression to get the stream // for the variable. if namedValueScope.HasLabel(proto.ScopeLabel_STREAMABLE_LOOP) { // Call .Stream() on the expression. streamableMember, _ := db.scopegraph.TypeGraph().StreamableType().GetMember("Stream") streamExpr := codedom.MemberCall( codedom.MemberReference(loopExpr, streamableMember, namedValue), streamableMember, []codedom.Expression{}, namedValue) streamVariable = codedom.VarDefinitionWithInit(streamVarName, streamExpr, node) } // Create variables to hold the named value (as requested in the SRG) and the loop result. namedVariable := codedom.VarDefinition(namedValueName, node) resultVariable := codedom.VarDefinition(resultVarName, node) // Create an expression statement to set the result variable to a call to Next(). streamMember, _ := db.scopegraph.TypeGraph().StreamType().GetMember("Next") nextCallExpr := codedom.MemberCall( codedom.MemberReference(codedom.LocalReference(streamVarName, node), streamMember, node), streamMember, []codedom.Expression{}, namedValue) resultExpressionStatement := codedom.ExpressionStatement(codedom.LocalAssignment(resultVarName, nextCallExpr, namedValue), namedValue) resultExpressionStatement.MarkReferenceable() // Set the continue statement to call Next() again. db.continueStatementMap[node.NodeId] = resultExpressionStatement // Create an expression statement to set the named variable to the first part of the tuple. namedExpressionStatement := codedom.ExpressionStatement( codedom.LocalAssignment(namedValueName, codedom.NativeAccess( codedom.LocalReference(resultVarName, namedValue), "First", namedValue), namedValue), namedValue) // Jump to the body state if the second part of the tuple in the result variable is true. bodyStart, bodyEnd := db.getStatements(node, parser.NodeLoopStatementBlock) checkJump := codedom.ConditionalJump( codedom.NativeAccess(codedom.LocalReference(resultVarName, node), "Second", node), bodyStart, finalStatement, node) // Steps: // 1) Empty statement // 2) Create the stream's variable (with no value) // 3) Create the named value variable (with no value) // 4) Create the result variable (with no value) // 5) (loop starts here) Pull the Next() result out of the stream // 6) Pull the true/false bool out of the result // 7) Pull the named value out of the result // 8) Jump based on the true/false boolean value codedom.AssignNextStatement(startStatement, streamVariable) codedom.AssignNextStatement(streamVariable, namedVariable) codedom.AssignNextStatement(namedVariable, resultVariable) codedom.AssignNextStatement(resultVariable, resultExpressionStatement) codedom.AssignNextStatement(resultExpressionStatement, namedExpressionStatement) codedom.AssignNextStatement(namedExpressionStatement, checkJump) // Jump to the result checking expression once the loop body completes. directJump := codedom.UnconditionalJump(resultExpressionStatement, node) codedom.AssignNextStatement(bodyEnd, directJump) return startStatement, finalStatement } else { bodyStart, bodyEnd := db.getStatements(node, parser.NodeLoopStatementBlock) // Loop over a direct boolean expression which is evaluated on each iteration. initialJump := codedom.ConditionalJump(loopExpr, bodyStart, finalStatement, node) directJump := codedom.UnconditionalJump(initialJump, node) codedom.AssignNextStatement(bodyEnd, directJump) codedom.AssignNextStatement(startStatement, initialJump) return startStatement, finalStatement } } else { bodyStart, bodyEnd := db.getStatements(node, parser.NodeLoopStatementBlock) // A loop without an expression just loops infinitely over the body. directJump := codedom.UnconditionalJump(bodyStart, node) codedom.AssignNextStatement(bodyEnd, directJump) codedom.AssignNextStatement(startStatement, bodyStart) return startStatement, directJump } }