Linker.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. class Block {
  2. constructor( node, parent = null ) {
  3. this.node = node;
  4. this.parent = parent;
  5. this.properties = {};
  6. }
  7. setProperty( name, value ) {
  8. this.properties[ name ] = value;
  9. }
  10. getProperty( name ) {
  11. let value = this.properties[ name ];
  12. if ( value === undefined && this.parent !== null ) {
  13. value = this.parent.getProperty( name );
  14. }
  15. return value;
  16. }
  17. }
  18. class Linker {
  19. constructor() {
  20. this.block = null;
  21. }
  22. addBlock( node ) {
  23. this.block = new Block( node, this.block );
  24. }
  25. removeBlock( node ) {
  26. if ( this.block === null || this.block.node !== node ) {
  27. throw new Error( 'No block to remove or block mismatch.' );
  28. }
  29. this.block = this.block.parent;
  30. }
  31. processVariables( node ) {
  32. this.block.setProperty( node.name, node );
  33. if ( node.value ) {
  34. this.processExpression( node.value );
  35. }
  36. }
  37. processUniform( node ) {
  38. this.block.setProperty( node.name, node );
  39. }
  40. processVarying( node ) {
  41. this.block.setProperty( node.name, node );
  42. }
  43. evalProperty( node ) {
  44. let property = '';
  45. if ( node.isAccessor ) {
  46. property += node.property;
  47. }
  48. return property;
  49. }
  50. processExpression( node ) {
  51. if ( node.isAccessor ) {
  52. const property = this.block.getProperty( this.evalProperty( node ) );
  53. if ( property ) {
  54. node.linker.reference = property;
  55. property.linker.accesses.push( node );
  56. }
  57. } else if ( node.isNumber || node.isString ) {
  58. // Process primitive values
  59. } else if ( node.isOperator ) {
  60. this.processExpression( node.left );
  61. this.processExpression( node.right );
  62. if ( node.isAssignment ) {
  63. const property = this.block.getProperty( this.evalProperty( node.left ) );
  64. if ( property ) {
  65. property.linker.assignments.push( node );
  66. }
  67. }
  68. } else if ( node.isFunctionCall ) {
  69. for ( const param of node.params ) {
  70. this.processExpression( param );
  71. }
  72. } else if ( node.isReturn ) {
  73. if ( node.value ) this.processExpression( node.value );
  74. } else if ( node.isDiscard || node.isBreak || node.isContinue ) {
  75. // Process control flow
  76. } else if ( node.isAccessorElements ) {
  77. this.processExpression( node.object );
  78. for ( const element of node.elements ) {
  79. this.processExpression( element.value );
  80. }
  81. } else if ( node.isDynamicElement || node.isStaticElement ) {
  82. this.processExpression( node.value );
  83. } else if ( node.isFor || node.isWhile ) {
  84. this.processForWhile( node );
  85. } else if ( node.isSwitch ) {
  86. this.processSwitch( node );
  87. } else if ( node.isVariableDeclaration ) {
  88. this.processVariables( node );
  89. } else if ( node.isUniform ) {
  90. this.processUniform( node );
  91. } else if ( node.isVarying ) {
  92. this.processVarying( node );
  93. } else if ( node.isTernary ) {
  94. this.processExpression( node.cond );
  95. this.processExpression( node.left );
  96. this.processExpression( node.right );
  97. } else if ( node.isConditional ) {
  98. this.processConditional( node );
  99. } else if ( node.isUnary ) {
  100. this.processExpression( node.expression );
  101. if ( node.isAssignment ) {
  102. if ( node.parent.hasAssignment !== true ) {
  103. // optimize increment/decrement operator
  104. // to avoid creating a new variable
  105. node.after = false;
  106. }
  107. const property = this.block.getProperty( this.evalProperty( node.expression ) );
  108. if ( property ) {
  109. property.linker.assignments.push( node );
  110. }
  111. }
  112. }
  113. }
  114. processBody( body ) {
  115. for ( const statement of body ) {
  116. this.processExpression( statement );
  117. }
  118. }
  119. processConditional( node ) {
  120. this.processExpression( node.cond );
  121. this.processBody( node.body );
  122. let current = node;
  123. while ( current.elseConditional ) {
  124. if ( current.elseConditional.cond ) {
  125. this.processExpression( current.elseConditional.cond );
  126. }
  127. this.processBody( current.elseConditional.body );
  128. current = current.elseConditional;
  129. }
  130. }
  131. processForWhile( node ) {
  132. if ( node.initialization ) this.processExpression( node.initialization );
  133. if ( node.condition ) this.processExpression( node.condition );
  134. if ( node.afterthought ) this.processExpression( node.afterthought );
  135. this.processBody( node.body );
  136. }
  137. processSwitch( switchNode ) {
  138. this.processExpression( switchNode.discriminant );
  139. for ( const switchCase of switchNode.cases ) {
  140. if ( switchCase.isDefault !== true ) {
  141. for ( const condition of switchCase.conditions ) {
  142. this.processExpression( condition );
  143. }
  144. }
  145. this.processBody( switchCase.body );
  146. }
  147. }
  148. processFunction( node ) {
  149. this.addBlock( node );
  150. for ( const param of node.params ) {
  151. this.block.setProperty( param.name, param );
  152. }
  153. this.processBody( node.body );
  154. this.removeBlock( node );
  155. }
  156. process( ast ) {
  157. this.addBlock( ast );
  158. for ( const statement of ast.body ) {
  159. if ( statement.isFunctionDeclaration ) {
  160. this.processFunction( statement );
  161. } else {
  162. this.processExpression( statement );
  163. }
  164. }
  165. this.removeBlock( ast );
  166. }
  167. }
  168. export default Linker;
粤ICP备19079148号