Skip to content
Permalink
Browse files
JEXL-369: move const/defined logic to lexical scope;
- restore explicit lexical shade through features;
- detect non-initialized const variables
  • Loading branch information
henrib committed May 16, 2022
1 parent 53b18ef commit 4ddf18313cadee541132d19beac62fcaa02f7321
Showing 9 changed files with 242 additions and 91 deletions.
@@ -54,7 +54,7 @@ public LexicalFrame(final Frame scriptf, final LexicalFrame outerf) {
* @param src the frame to copy
*/
public LexicalFrame(final LexicalFrame src) {
super(src.symbols, src.moreSymbols);
super(src);
frame = src.frame;
previous = src.previous;
stack = src.stack != null ? new ArrayDeque<>(src.stack) : null;
@@ -20,22 +20,35 @@

/**
* The set of symbols declared in a lexical scope.
* <p>The symbol identifiers are determined by the functional scope.
* <p>The symbol identifiers are determined by the functional scope.</p>
* <p>We use 3 bits per symbol; bit 0 sets the actual symbol as lexical (let),
* bit 1 as a const, bit 2 as a defined (valued) const.
* There are actually only 4 used states: 0, 1, 3, 7</p>
*/
public class LexicalScope {
/**
* Number of bits in a long.
*/
protected static final int LONGBITS = 64;
/**
* The mask of symbols in the frame.
* Bits per symbol.
* Declared, const, defined.
*/
protected static final int BITS_PER_SYMBOL = 3;
/**
* Number of symbols.
*/
protected int count = 0;
/**
* The mask of symbols in the scope.
*/
protected long symbols = 0L;
/**
* Symbols after 64.
*/
protected BitSet moreSymbols = null;


/**
* Create a scope.
*/
@@ -44,54 +57,51 @@ public LexicalScope() {

/**
* Frame copy ctor base.
*
* @param s the symbols mask
* @param ms the more symbols bitset
*/
protected LexicalScope(final long s, final BitSet ms) {
symbols = s;
protected LexicalScope(LexicalScope other) {
BitSet ms;
symbols = other.symbols;
ms = other.moreSymbols;
moreSymbols = ms != null ? (BitSet) ms.clone() : null;
}

/**
* Ensure more symbpls can be stored.
* Ensures more symbols can be stored.
*
* @return the set of more symbols
*/
protected final BitSet moreSymbols() {
private BitSet moreSymbols() {
if (moreSymbols == null) {
moreSymbols = new BitSet();
}
return moreSymbols;
}

/**
* Checks whether a symbol has already been declared.
*
* @param symbol the symbol
* @return true if declared, false otherwise
* Whether a given bit (not symbol) is set.
* @param bit the bit
* @return true if set
*/
public boolean hasSymbol(final int symbol) {
if (symbol < LONGBITS) {
return (symbols & (1L << symbol)) != 0L;
private boolean isSet(final int bit) {
if (bit < LONGBITS) {
return (symbols & (1L << bit)) != 0L;
}
return moreSymbols != null && moreSymbols.get(symbol - LONGBITS);
return moreSymbols != null && moreSymbols.get(bit - LONGBITS);
}

/**
* Adds a symbol in this scope.
*
* @param symbol the symbol
* @return true if registered, false if symbol was already registered
* Sets a given bit (not symbol).
* @param bit the bit
* @return true if it was actually set, false if it was set before
*/
public boolean addSymbol(final int symbol) {
if (symbol < LONGBITS) {
if ((symbols & (1L << symbol)) != 0L) {
private boolean set(final int bit) {
if (bit < LONGBITS) {
if ((symbols & (1L << bit)) != 0L) {
return false;
}
symbols |= (1L << symbol);
symbols |= (1L << bit);
} else {
final int s = symbol - LONGBITS;
final int s = bit - LONGBITS;
final BitSet ms = moreSymbols();
if (ms.get(s)) {
return false;
@@ -101,6 +111,77 @@ public boolean addSymbol(final int symbol) {
return true;
}

/**
* Checks whether a symbol has already been declared.
*
* @param symbol the symbol
* @return true if declared, false otherwise
*/
public boolean hasSymbol(final int symbol) {
final int bit = symbol << BITS_PER_SYMBOL;
return isSet(bit);
}

/**
* Checks whether a symbol is declared as a constant.
*
* @param symbol the symbol
* @return true if declared as constant, false otherwise
*/
public boolean isConstant(final int symbol) {
final int bit = (symbol << BITS_PER_SYMBOL) | 1;
return isSet(bit);
}

/**
* Checks whether a const symbol has been defined, ie has a value.
*
* @param symbol the symbol
* @return true if defined, false otherwise
*/
public boolean isDefined(final int symbol) {
final int bit = (symbol << BITS_PER_SYMBOL) | 2;
return isSet(bit);

}

/**
* Adds a symbol in this scope.
*
* @param symbol the symbol
* @return true if registered, false if symbol was already registered
*/
public boolean addSymbol(final int symbol) {
final int bit = (symbol << BITS_PER_SYMBOL) ;
if (set(bit)) {
count += 1;
return true;
}
return false;
}

/**
* Adds a constant in this scope.
*
* @param symbol the symbol
* @return true if registered, false if symbol was already registered
*/
public boolean addConstant(final int symbol) {
final int bit = (symbol << BITS_PER_SYMBOL) | 1;
return set(bit);
}

/**
* Defines a constant in this scope.
*
* @param symbol the symbol
* @return true if registered, false if symbol was already registered
*/
public boolean defineSymbol(final int symbol) {
final int bit = (symbol << BITS_PER_SYMBOL) | 2;
return set(bit);
}

/**
* Clear all symbols.
*
@@ -112,14 +193,16 @@ public final void clearSymbols(final java.util.function.IntConsumer cleanSymbol)
long clean = symbols;
while (clean != 0L) {
final int s = Long.numberOfTrailingZeros(clean);
clean &= ~(1L << s);
cleanSymbol.accept(s);
// call clean for symbol definition (7 as a mask for 3 bits,1+2+4)
clean &= ~(7L << s);
cleanSymbol.accept(s >> BITS_PER_SYMBOL);
}
}
symbols = 0L;
if (moreSymbols != null) {
if (cleanSymbol != null) {
for (int s = moreSymbols.nextSetBit(0); s != -1; s = moreSymbols.nextSetBit(s + 1)) {
// step over const and definition (3 bits per symbol)
for (int s = moreSymbols.nextSetBit(0); s != -1; s = moreSymbols.nextSetBit(s + BITS_PER_SYMBOL)) {
cleanSymbol.accept(s + LONGBITS);
}
}
@@ -131,6 +214,6 @@ public final void clearSymbols(final java.util.function.IntConsumer cleanSymbol)
* @return the number of symbols defined in this scope.
*/
public int getSymbolCount() {
return Long.bitCount(symbols) + (moreSymbols == null ? 0 : moreSymbols.cardinality());
return count;
}
}
@@ -66,10 +66,6 @@ public final class Scope {
* The map of local captured variables to parent scope variables, ie closure.
*/
private Map<Integer, Integer> capturedVariables = null;
/**
* Const symbols.
*/
private LexicalScope constVariables = null;
/**
* Let symbols.
*/
@@ -178,27 +174,6 @@ public boolean isLexical(int s) {
return lexicalVariables != null && s >= 0 && lexicalVariables.hasSymbol(s);
}

/**
* Marks a symbol as a const.
* @param s the symbol
* @return true if the symbol was not already present in the constant set
*/
public boolean addConstant(int s) {
if (constVariables == null) {
constVariables = new LexicalScope();
}
return constVariables.addSymbol(s);
}

/**
* Checks whether a symbol is declared through a const.
* @param s the symbol
* @return true if symbol was declared through const
*/
public boolean isConstant(int s) {
return constVariables != null && s >= 0 && constVariables.hasSymbol(s);
}

/**
* Checks whether a given symbol is captured.
* @param symbol the symbol number
@@ -34,6 +34,8 @@ public class ASTIdentifier extends JexlNode {
private static final int LEXICAL = 3;
/** The const variable flag. */
private static final int CONST = 4;
/** The defined variable flag. */
private static final int DEFINED = 5;

ASTIdentifier(final int id) {
super(id);
@@ -125,6 +127,14 @@ public void setConstant(final boolean f) {
flags = set(CONST, flags, f);
}

public boolean isDefined() {
return isSet(DEFINED, flags);
}

public void setDefined(final boolean f) {
flags = set(DEFINED, flags, f);
}

public String getName() {
return name;
}
@@ -23,7 +23,7 @@
* @since 3.2
*/
public class JexlLexicalNode extends JexlNode implements JexlParser.LexicalUnit {
private LexicalScope locals = null;
private LexicalScope lexicalScope = null;

public JexlLexicalNode(final int id) {
super(id);
@@ -35,24 +35,50 @@ public JexlLexicalNode(final Parser p, final int id) {

@Override
public boolean declareSymbol(final int symbol) {
if (locals == null) {
locals = new LexicalScope();
if (lexicalScope == null) {
lexicalScope = new LexicalScope();
}
return locals.addSymbol(symbol);
return lexicalScope.addSymbol(symbol);
}

@Override
public boolean isConstant(final int symbol) {
return lexicalScope != null && lexicalScope.isConstant(symbol);
}

@Override
public boolean isDefined(final int symbol) {
return lexicalScope != null && lexicalScope.isDefined(symbol);
}

@Override
public void setConstant(int symbol) {
lexicalScope.addConstant(symbol);
}

@Override
public void setDefined(int symbol) {
lexicalScope.defineSymbol(symbol);
}

@Override
public int getSymbolCount() {
return locals == null? 0 : locals.getSymbolCount();
return lexicalScope == null? 0 : lexicalScope.getSymbolCount();
}

@Override
public boolean hasSymbol(final int symbol) {
return locals != null && locals.hasSymbol(symbol);
return lexicalScope != null && lexicalScope.hasSymbol(symbol);
}

@Override
public LexicalScope getLexicalScope() {
return locals;
return lexicalScope;
}


@Override
public void jjtClose() {

}
}

0 comments on commit 4ddf183

Please sign in to comment.