Skip to content

⬅️ Back to Table of Contents

📄 Linker.js

📊 Analysis Summary

Metric Count
🔧 Functions 15
🧱 Classes 2
📊 Variables & Constants 3

📚 Table of Contents

🛠️ File Location:

📂 examples/jsm/transpiler/Linker.js

Variables & Constants

Name Type Kind Value Exported
value any let/var this.properties[ name ]
property string let/var ''
current any let/var node

Functions

Block.setProperty(name: any, value: any): void

Parameters:

  • name any
  • value any

Returns: void

Code
setProperty( name, value ) {

        this.properties[ name ] = value;

    }

Block.getProperty(name: any): any

Parameters:

  • name any

Returns: any

Calls:

  • this.parent.getProperty
Code
getProperty( name ) {

        let value = this.properties[ name ];

        if ( value === undefined && this.parent !== null ) {

            value = this.parent.getProperty( name );

        }

        return value;

    }

Linker.addBlock(node: any): void

Parameters:

  • node any

Returns: void

Code
addBlock( node ) {

        this.block = new Block( node, this.block );

    }

Linker.removeBlock(node: any): void

Parameters:

  • node any

Returns: void

Code
removeBlock( node ) {

        if ( this.block === null || this.block.node !== node ) {

            throw new Error( 'No block to remove or block mismatch.' );

        }

        this.block = this.block.parent;

    }

Linker.processVariables(node: any): void

Parameters:

  • node any

Returns: void

Calls:

  • this.block.setProperty
  • this.processExpression
Code
processVariables( node ) {

        this.block.setProperty( node.name, node );

        if ( node.value ) {

            this.processExpression( node.value );

        }

    }

Linker.processUniform(node: any): void

Parameters:

  • node any

Returns: void

Calls:

  • this.block.setProperty
Code
processUniform( node ) {

        this.block.setProperty( node.name, node );

    }

Linker.processVarying(node: any): void

Parameters:

  • node any

Returns: void

Calls:

  • this.block.setProperty
Code
processVarying( node ) {

        this.block.setProperty( node.name, node );

    }

Linker.evalProperty(node: any): string

Parameters:

  • node any

Returns: string

Code
evalProperty( node ) {

        let property = '';

        if ( node.isAccessor ) {

            property += node.property;

        }

        return property;

    }

Linker.processExpression(node: any): void

Parameters:

  • node any

Returns: void

Calls:

  • this.block.getProperty
  • this.evalProperty
  • property.linker.accesses.push
  • this.processExpression
  • property.linker.assignments.push
  • this.processForWhile
  • this.processSwitch
  • this.processVariables
  • this.processUniform
  • this.processVarying
  • this.processConditional

Internal Comments:

// optimize increment/decrement operator (x4)
// to avoid creating a new variable (x4)

Code
processExpression( node ) {

        if ( node.isAccessor ) {

            const property = this.block.getProperty( this.evalProperty( node ) );

            if ( property ) {

                node.linker.reference = property;

                property.linker.accesses.push( node );

            }

        } else if ( node.isNumber || node.isString ) {

            // Process primitive values

        } else if ( node.isOperator ) {

            this.processExpression( node.left );
            this.processExpression( node.right );

            if ( node.isAssignment ) {

                const property = this.block.getProperty( this.evalProperty( node.left ) );

                if ( property ) {

                    property.linker.assignments.push( node );

                }

            }

        } else if ( node.isFunctionCall ) {

            for ( const param of node.params ) {

                this.processExpression( param );

            }

        } else if ( node.isReturn ) {

            if ( node.value ) this.processExpression( node.value );

        } else if ( node.isDiscard || node.isBreak || node.isContinue ) {

            // Process control flow

        } else if ( node.isAccessorElements ) {

            this.processExpression( node.object );

            for ( const element of node.elements ) {

                this.processExpression( element.value );

            }

        } else if ( node.isDynamicElement || node.isStaticElement ) {

            this.processExpression( node.value );

        } else if ( node.isFor || node.isWhile ) {

            this.processForWhile( node );

        } else if ( node.isSwitch ) {

            this.processSwitch( node );

        } else if ( node.isVariableDeclaration ) {

            this.processVariables( node );

        } else if ( node.isUniform ) {

            this.processUniform( node );

        } else if ( node.isVarying ) {

            this.processVarying( node );

        } else if ( node.isTernary ) {

            this.processExpression( node.cond );
            this.processExpression( node.left );
            this.processExpression( node.right );

        } else if ( node.isConditional ) {

            this.processConditional( node );

        } else if ( node.isUnary ) {

            this.processExpression( node.expression );

            if ( node.isAssignment ) {

                if ( node.parent.hasAssignment !== true ) {

                    // optimize increment/decrement operator
                    // to avoid creating a new variable

                    node.after = false;

                }

                const property = this.block.getProperty( this.evalProperty( node.expression ) );

                if ( property ) {

                    property.linker.assignments.push( node );

                }

            }

        }

    }

Linker.processBody(body: any): void

Parameters:

  • body any

Returns: void

Calls:

  • this.processExpression
Code
processBody( body ) {

        for ( const statement of body ) {

            this.processExpression( statement );

        }

    }

Linker.processConditional(node: any): void

Parameters:

  • node any

Returns: void

Calls:

  • this.processExpression
  • this.processBody
Code
processConditional( node ) {

        this.processExpression( node.cond );
        this.processBody( node.body );

        let current = node;

        while ( current.elseConditional ) {

            if ( current.elseConditional.cond ) {

                this.processExpression( current.elseConditional.cond );

            }

            this.processBody( current.elseConditional.body );

            current = current.elseConditional;

        }

    }

Linker.processForWhile(node: any): void

Parameters:

  • node any

Returns: void

Calls:

  • this.processExpression
  • this.processBody
Code
processForWhile( node ) {

        if ( node.initialization ) this.processExpression( node.initialization );
        if ( node.condition ) this.processExpression( node.condition );
        if ( node.afterthought ) this.processExpression( node.afterthought );

        this.processBody( node.body );

    }

Linker.processSwitch(switchNode: any): void

Parameters:

  • switchNode any

Returns: void

Calls:

  • this.processExpression
  • this.processBody
Code
processSwitch( switchNode ) {

        this.processExpression( switchNode.discriminant );

        for ( const switchCase of switchNode.cases ) {

            if ( switchCase.isDefault !== true ) {

                for ( const condition of switchCase.conditions ) {

                    this.processExpression( condition );

                }

            }

            this.processBody( switchCase.body );

        }

    }

Linker.processFunction(node: any): void

Parameters:

  • node any

Returns: void

Calls:

  • this.addBlock
  • this.block.setProperty
  • this.processBody
  • this.removeBlock
Code
processFunction( node ) {

        this.addBlock( node );

        for ( const param of node.params ) {

            this.block.setProperty( param.name, param );

        }

        this.processBody( node.body );

        this.removeBlock( node );

    }

Linker.process(ast: any): void

Parameters:

  • ast any

Returns: void

Calls:

  • this.addBlock
  • this.processFunction
  • this.processExpression
  • this.removeBlock
Code
process( ast ) {

        this.addBlock( ast );

        for ( const statement of ast.body ) {

            if ( statement.isFunctionDeclaration ) {

                this.processFunction( statement );

            } else {

                this.processExpression( statement );

            }

        }

        this.removeBlock( ast );

    }

Classes

Block

Class Code
class Block {

    constructor( node, parent = null ) {

        this.node = node;
        this.parent = parent;

        this.properties = {};

    }

    setProperty( name, value ) {

        this.properties[ name ] = value;

    }

    getProperty( name ) {

        let value = this.properties[ name ];

        if ( value === undefined && this.parent !== null ) {

            value = this.parent.getProperty( name );

        }

        return value;

    }

}

Methods

setProperty(name: any, value: any): void
Code
setProperty( name, value ) {

        this.properties[ name ] = value;

    }
getProperty(name: any): any
Code
getProperty( name ) {

        let value = this.properties[ name ];

        if ( value === undefined && this.parent !== null ) {

            value = this.parent.getProperty( name );

        }

        return value;

    }

Linker

Class Code
class Linker {

    constructor() {

        this.block = null;

    }

    addBlock( node ) {

        this.block = new Block( node, this.block );

    }

    removeBlock( node ) {

        if ( this.block === null || this.block.node !== node ) {

            throw new Error( 'No block to remove or block mismatch.' );

        }

        this.block = this.block.parent;

    }

    processVariables( node ) {

        this.block.setProperty( node.name, node );

        if ( node.value ) {

            this.processExpression( node.value );

        }

    }

    processUniform( node ) {

        this.block.setProperty( node.name, node );

    }

    processVarying( node ) {

        this.block.setProperty( node.name, node );

    }

    evalProperty( node ) {

        let property = '';

        if ( node.isAccessor ) {

            property += node.property;

        }

        return property;

    }

    processExpression( node ) {

        if ( node.isAccessor ) {

            const property = this.block.getProperty( this.evalProperty( node ) );

            if ( property ) {

                node.linker.reference = property;

                property.linker.accesses.push( node );

            }

        } else if ( node.isNumber || node.isString ) {

            // Process primitive values

        } else if ( node.isOperator ) {

            this.processExpression( node.left );
            this.processExpression( node.right );

            if ( node.isAssignment ) {

                const property = this.block.getProperty( this.evalProperty( node.left ) );

                if ( property ) {

                    property.linker.assignments.push( node );

                }

            }

        } else if ( node.isFunctionCall ) {

            for ( const param of node.params ) {

                this.processExpression( param );

            }

        } else if ( node.isReturn ) {

            if ( node.value ) this.processExpression( node.value );

        } else if ( node.isDiscard || node.isBreak || node.isContinue ) {

            // Process control flow

        } else if ( node.isAccessorElements ) {

            this.processExpression( node.object );

            for ( const element of node.elements ) {

                this.processExpression( element.value );

            }

        } else if ( node.isDynamicElement || node.isStaticElement ) {

            this.processExpression( node.value );

        } else if ( node.isFor || node.isWhile ) {

            this.processForWhile( node );

        } else if ( node.isSwitch ) {

            this.processSwitch( node );

        } else if ( node.isVariableDeclaration ) {

            this.processVariables( node );

        } else if ( node.isUniform ) {

            this.processUniform( node );

        } else if ( node.isVarying ) {

            this.processVarying( node );

        } else if ( node.isTernary ) {

            this.processExpression( node.cond );
            this.processExpression( node.left );
            this.processExpression( node.right );

        } else if ( node.isConditional ) {

            this.processConditional( node );

        } else if ( node.isUnary ) {

            this.processExpression( node.expression );

            if ( node.isAssignment ) {

                if ( node.parent.hasAssignment !== true ) {

                    // optimize increment/decrement operator
                    // to avoid creating a new variable

                    node.after = false;

                }

                const property = this.block.getProperty( this.evalProperty( node.expression ) );

                if ( property ) {

                    property.linker.assignments.push( node );

                }

            }

        }

    }

    processBody( body ) {

        for ( const statement of body ) {

            this.processExpression( statement );

        }

    }

    processConditional( node ) {

        this.processExpression( node.cond );
        this.processBody( node.body );

        let current = node;

        while ( current.elseConditional ) {

            if ( current.elseConditional.cond ) {

                this.processExpression( current.elseConditional.cond );

            }

            this.processBody( current.elseConditional.body );

            current = current.elseConditional;

        }

    }

    processForWhile( node ) {

        if ( node.initialization ) this.processExpression( node.initialization );
        if ( node.condition ) this.processExpression( node.condition );
        if ( node.afterthought ) this.processExpression( node.afterthought );

        this.processBody( node.body );

    }

    processSwitch( switchNode ) {

        this.processExpression( switchNode.discriminant );

        for ( const switchCase of switchNode.cases ) {

            if ( switchCase.isDefault !== true ) {

                for ( const condition of switchCase.conditions ) {

                    this.processExpression( condition );

                }

            }

            this.processBody( switchCase.body );

        }

    }

    processFunction( node ) {

        this.addBlock( node );

        for ( const param of node.params ) {

            this.block.setProperty( param.name, param );

        }

        this.processBody( node.body );

        this.removeBlock( node );

    }

    process( ast ) {

        this.addBlock( ast );

        for ( const statement of ast.body ) {

            if ( statement.isFunctionDeclaration ) {

                this.processFunction( statement );

            } else {

                this.processExpression( statement );

            }

        }

        this.removeBlock( ast );

    }

}

Methods

addBlock(node: any): void
Code
addBlock( node ) {

        this.block = new Block( node, this.block );

    }
removeBlock(node: any): void
Code
removeBlock( node ) {

        if ( this.block === null || this.block.node !== node ) {

            throw new Error( 'No block to remove or block mismatch.' );

        }

        this.block = this.block.parent;

    }
processVariables(node: any): void
Code
processVariables( node ) {

        this.block.setProperty( node.name, node );

        if ( node.value ) {

            this.processExpression( node.value );

        }

    }
processUniform(node: any): void
Code
processUniform( node ) {

        this.block.setProperty( node.name, node );

    }
processVarying(node: any): void
Code
processVarying( node ) {

        this.block.setProperty( node.name, node );

    }
evalProperty(node: any): string
Code
evalProperty( node ) {

        let property = '';

        if ( node.isAccessor ) {

            property += node.property;

        }

        return property;

    }
processExpression(node: any): void
Code
processExpression( node ) {

        if ( node.isAccessor ) {

            const property = this.block.getProperty( this.evalProperty( node ) );

            if ( property ) {

                node.linker.reference = property;

                property.linker.accesses.push( node );

            }

        } else if ( node.isNumber || node.isString ) {

            // Process primitive values

        } else if ( node.isOperator ) {

            this.processExpression( node.left );
            this.processExpression( node.right );

            if ( node.isAssignment ) {

                const property = this.block.getProperty( this.evalProperty( node.left ) );

                if ( property ) {

                    property.linker.assignments.push( node );

                }

            }

        } else if ( node.isFunctionCall ) {

            for ( const param of node.params ) {

                this.processExpression( param );

            }

        } else if ( node.isReturn ) {

            if ( node.value ) this.processExpression( node.value );

        } else if ( node.isDiscard || node.isBreak || node.isContinue ) {

            // Process control flow

        } else if ( node.isAccessorElements ) {

            this.processExpression( node.object );

            for ( const element of node.elements ) {

                this.processExpression( element.value );

            }

        } else if ( node.isDynamicElement || node.isStaticElement ) {

            this.processExpression( node.value );

        } else if ( node.isFor || node.isWhile ) {

            this.processForWhile( node );

        } else if ( node.isSwitch ) {

            this.processSwitch( node );

        } else if ( node.isVariableDeclaration ) {

            this.processVariables( node );

        } else if ( node.isUniform ) {

            this.processUniform( node );

        } else if ( node.isVarying ) {

            this.processVarying( node );

        } else if ( node.isTernary ) {

            this.processExpression( node.cond );
            this.processExpression( node.left );
            this.processExpression( node.right );

        } else if ( node.isConditional ) {

            this.processConditional( node );

        } else if ( node.isUnary ) {

            this.processExpression( node.expression );

            if ( node.isAssignment ) {

                if ( node.parent.hasAssignment !== true ) {

                    // optimize increment/decrement operator
                    // to avoid creating a new variable

                    node.after = false;

                }

                const property = this.block.getProperty( this.evalProperty( node.expression ) );

                if ( property ) {

                    property.linker.assignments.push( node );

                }

            }

        }

    }
processBody(body: any): void
Code
processBody( body ) {

        for ( const statement of body ) {

            this.processExpression( statement );

        }

    }
processConditional(node: any): void
Code
processConditional( node ) {

        this.processExpression( node.cond );
        this.processBody( node.body );

        let current = node;

        while ( current.elseConditional ) {

            if ( current.elseConditional.cond ) {

                this.processExpression( current.elseConditional.cond );

            }

            this.processBody( current.elseConditional.body );

            current = current.elseConditional;

        }

    }
processForWhile(node: any): void
Code
processForWhile( node ) {

        if ( node.initialization ) this.processExpression( node.initialization );
        if ( node.condition ) this.processExpression( node.condition );
        if ( node.afterthought ) this.processExpression( node.afterthought );

        this.processBody( node.body );

    }
processSwitch(switchNode: any): void
Code
processSwitch( switchNode ) {

        this.processExpression( switchNode.discriminant );

        for ( const switchCase of switchNode.cases ) {

            if ( switchCase.isDefault !== true ) {

                for ( const condition of switchCase.conditions ) {

                    this.processExpression( condition );

                }

            }

            this.processBody( switchCase.body );

        }

    }
processFunction(node: any): void
Code
processFunction( node ) {

        this.addBlock( node );

        for ( const param of node.params ) {

            this.block.setProperty( param.name, param );

        }

        this.processBody( node.body );

        this.removeBlock( node );

    }
process(ast: any): void
Code
process( ast ) {

        this.addBlock( ast );

        for ( const statement of ast.body ) {

            if ( statement.isFunctionDeclaration ) {

                this.processFunction( statement );

            } else {

                this.processExpression( statement );

            }

        }

        this.removeBlock( ast );

    }