/*
* CDDL HEADER START
*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2012, 2016 by Delphix. All rights reserved.
*/
/*
* Syntactic sugar features are implemented by transforming the D parse tree
* such that it only uses the subset of D that is supported by the rest of the
* compiler / the kernel. A clause containing these language features is
* referred to as a "super-clause", and its transformation typically entails
* creating several "sub-clauses" to implement it. For diagnosability, the
* sub-clauses will be printed if the "-xtree=8" flag is specified.
*
* basic block (e.g. the body of the "if" and "else" statements, and the
* statements before and after) is turned into its own sub-clause, with a
* predicate that causes it to be executed only if the code flows to this point.
*
* This infrastructure is designed to accommodate other syntactic sugar features
* in the future.
*/
#include <sys/sysmacros.h>
#include <assert.h>
#include <strings.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <dt_module.h>
#include <dt_program.h>
#include <dt_provider.h>
#include <dt_printf.h>
#include <dt_pid.h>
#include <dt_grammar.h>
#include <dt_ident.h>
#include <dt_string.h>
#include <dt_impl.h>
typedef struct dt_sugar_parse {
/*
* Return a node for "self->%error".
*
* Note that the "%" is part of the variable name, and is included so that
* this variable name can not collide with any user-specified variable.
*
* This error variable is used to keep track of if there has been an error
* in any of the sub-clauses, and is used to prevent execution of subsequent
* sub-clauses following an error.
*/
static dt_node_t *
dt_sugar_new_error_var(void)
{
}
/*
* Append this clause to the clause list.
*/
static void
{
}
/*
* Prepend this clause to the clause list.
*/
static void
{
}
/*
* Return a node for "this->%condition_<condid>", or NULL if condid==0.
*
* Note that the "%" is part of the variable name, and is included so that
* this variable name can not collide with any user-specified variable.
*/
static dt_node_t *
{
char *str;
if (condid == 0)
return (NULL);
dt_node_ident(str)));
}
/*
* Return new clause to evaluate predicate and set newcond. condid is
* the condition that we are already under, or 0 if none.
* The new clause will be of the form:
*
* dp_pdescs
* /!self->%error/
* {
* this->%condition_<newcond> =
* (this->%condition_<condid> && pred);
* }
*
* Note: if condid==0, we will instead do "... = (1 && pred)", to effectively
* convert the pred to a boolean.
*
* Note: Unless an error has been encountered, we always set the condition
* variable (either to 0 or 1). This lets us avoid resetting the condition
* variables back to 0 when the super-clause completes.
*/
static dt_node_t *
{
/* predicate is !self->%error */
if (condid == 0) {
/*
* value is (1 && pred)
*
* Note, D doesn't allow a probe-local "this" variable to
* be reused as a different type, even from a different probe.
* Therefore, value can't simply be <pred>, because then
* its type could be different when we reuse this condid
* in a different meta-clause.
*/
} else {
/* value is (this->%condition_<condid> && pred) */
}
/* body is "this->%condition_<retval> = <value>;" */
}
/*
* Generate a new clause to evaluate predicate and set a new condition variable,
* whose ID will be returned. The new clause will be appended to
* dp_first_new_clause.
*/
static int
{
return (dp->dtsp_num_conditions);
}
/*
* Visit the specified node and all of its descendants. Currently this is only
* used to count the number of "if" statements (dtsp_num_ifs).
*/
static void
{
case DT_NODE_FREE:
case DT_NODE_INT:
case DT_NODE_STRING:
case DT_NODE_SYM:
case DT_NODE_TYPE:
case DT_NODE_PROBE:
case DT_NODE_PDESC:
case DT_NODE_IDENT:
break;
case DT_NODE_FUNC:
break;
case DT_NODE_OP1:
break;
case DT_NODE_OP2:
}
}
break;
case DT_NODE_OP3:
break;
case DT_NODE_DEXPR:
case DT_NODE_DFUNC:
break;
case DT_NODE_AGG:
break;
case DT_NODE_CLAUSE:
break;
case DT_NODE_INLINE: {
break;
}
case DT_NODE_MEMBER:
if (dnp->dn_membexpr)
break;
case DT_NODE_XLATOR:
break;
case DT_NODE_PROVIDER:
break;
case DT_NODE_PROG:
break;
case DT_NODE_IF:
dp->dtsp_num_ifs++;
break;
default:
}
}
/*
* Return a new clause which resets the error variable to zero:
*
* dp_pdescs{ self->%error = 0; }
*
* This clause will be executed at the beginning of each meta-clause, to
* ensure the error variable is unset (in case the previous meta-clause
* failed).
*/
static dt_node_t *
{
dt_sugar_new_error_var(), dt_node_int(0)));
}
/*
* Evaluate the conditional, and recursively visit the body of the "if"
* statement (and the "else", if present).
*/
static void
{
int newid;
/* condition */
/* body of if */
/*
* Visit the body of the "else" statement, if present. Note that we
* generate a new condition which is the inverse of the previous
* condition.
*/
}
}
/*
* Generate a new clause to evaluate the statements based on the condition.
* The new clause will be appended to dp_first_new_clause.
*
* dp_pdescs
* /!self->%error && this->%condition_<condid>/
* {
* stmts
* }
*/
static void
{
if (condid == 0) {
/*
* Don't bother with !error on the first clause, because if
* there is only one clause, we don't add the prelude to
* zero out %error.
*/
if (dp->dtsp_num_conditions != 0) {
}
} else {
}
}
/*
* Visit all the statements in this list, and break them into basic blocks,
* generating new clauses for "if" and "else" statements.
*/
static void
{
if (first_stmt_in_basic_block == NULL)
continue;
}
/*
* Remove this and following statements from the previous
* clause.
*/
/*
* Generate clause for statements preceding the "if"
*/
if (first_stmt_in_basic_block != NULL) {
}
}
/* generate clause for statements after last "if". */
if (first_stmt_in_basic_block != NULL) {
}
}
/*
* Generate a new clause which will set the error variable when an error occurs.
* Only one of these clauses is created per program (e.g. script file).
* The clause is:
*
* dtrace:::ERROR{ self->%error = 1; }
*/
static dt_node_t *
dt_sugar_makeerrorclause(void)
{
}
/*
* Transform the super-clause into straight-D, returning the new list of
* sub-clauses.
*/
{
int condid = 0;
/* make dt_node_int() generate an "int"-typed integer */
yyintsuffix[0] = '\0';
yyintprefix = 0;
/*
* There is nothing that modifies the number of clauses. Use
* the existing clause as-is, with its predicate intact. This
* ensures that in the absence of D sugar, the body of the
* clause can create a variable that is referenced in the
* predicate.
*/
} else {
}
/*
* dt_sugar_visit_stmts() does not emit a clause with
* an empty body (e.g. if there's an empty "if" body),
* but we need the empty body here so that we
* continue to get the default tracing action.
*/
} else {
}
}
if (dp.dtsp_num_conditions != 0) {
}
}
return (dp.dtsp_clause_list);
}