<commit_before>// Load in our dependencies
var assert = require('assert');
var ecmaScopes = require('ecma-scopes');
var estraverse = require('estraverse');

// Define constants
var SCOPE_INFO_DECLARED = {
  YES: true,
  UNKNOWN: 'unknown',
  NO: false
};
var SCOPE_INFO_INSIDE_WITH = {
  YES: true,
  NO: false
};
var SCOPE_INFO_USED_IN_A_WITH = {
  YES: true,
  NO: false
};
var SCOPE_INFO_TOP_LEVEL = {
  YES: true,
  NO: false
};
var SCOPE_INFO_TYPES = {
  LEXICAL: 'lexical',
  BLOCK: 'block',
  UNDECLARED: 'undeclared'
};
var SCOPE_TYPES = {
  LEXICAL: 'lexical',
  WITH: 'with',
  BLOCK: 'block'
};

// Define helper to check a new scope
function getScopeType(node) {
  if (node.type === 'Program') {
    // DEV: We mark `program` as `lexical` because it is easier to set/compare against
    return SCOPE_TYPES.LEXICAL;
  } else if (ecmaScopes.lexical.indexOf(node.type) !== -1) {
    return SCOPE_TYPES.LEXICAL;
  } else if (node.type === 'WithStatement') {
    return SCOPE_TYPES.WITH;
  } else if (ecmaScopes.block.indexOf(node.type) !== -1) {
    return SCOPE_TYPES.BLOCK;
  } else {
    return null;
  }
}

// TODO: Move to an object or reduce the parameters. The count is too damn high
// DEV: We specify `baseNode` because it should be where we resolve the nearest scope from which may not be an identifier
function resolveScope(baseNode, identifiers, scopeType) {
  // If we resolved identifiers
  // DEV: This assert is to make it faster for developers to debug
  assert(Array.isArray(identifiers), 'Identifier resolution for baseNode "' + baseNode.type + '" did not result in an array');

  if (identifiers.length) {
    // Walk up the nearest scopes until we find one that matches our type
    // DEV: Use `scopeTypes` since a block scope can hit `Program` which is lexical
    var scope = baseNode._nearestScope;
    var scopeTypes = scopeType === SCOPE_INFO_TYPES.LEXICAL ? [SCOPE_INFO_TYPES.LEXICAL] : [SCOPE_INFO_TYPES.LEXICAL, SCOPE_INFO_TYPES.BLOCK];
    while (scope) {
      // If the scope type matches, stop
      if (scopeTypes.indexOf(scope.type) !== -1) {
        break;
      }

      // Look at the scope's parent
      scope = scope.parent;
    }

    // Expand all identifiers (e.g. object patterns to variables, `var {world}` -> `world`)
    var expandedIdentifiers = [];
    identifiers.forEach(function expandIdentifier (identifier) {
      // If it is an object pattern, expand it and save each key/identifier
      if (identifier.type === 'ObjectPattern') {
        identifier.properties.forEach(function extractObjectIdentifier (property) {
          expandedIdentifiers.push(property.key);
        });
      // Otherwise if it is an array pattern, expand it and save each identifier
      } else if (identifier.type === 'ArrayPattern') {
        identifier.elements.forEach(function extractArrayIdentifier (element) {
          expandedIdentifiers.push(element);
        });
      // Otherwise, save the original identifier
      } else {
        expandedIdentifiers.push(identifier);
      }
    });

    // Save declaration info on the scope and scope type to the identifier
    expandedIdentifiers.forEach(function saveIdentifier (identifier) {
      scope.identifiers[identifier.name] = identifier;
      identifier._scopeType = scopeType;
    });
  }
}

function walkVariables(ast, params) {
  // Walk entire tree
  estraverse.traverse(ast, {
    // Skip over undesired identifiers (e.g. `log` in `console.log`)
    // https://github.com/Constellation/estraverse/blob/1.7.0/estraverse.js#L222-L288
    keys: {
      BreakStatement: [/*'label'*/], // `loop1` in `break loop1;`
      ContinueStatement: [/*'label'*/], // `loop1` in `continue loop1;`
      LabeledStatement: [/*'label',*/ 'body'], // `loop1` in `loop1: while (true) {}`
      MemberExpression: ['object'/*, 'property'*/], // `log` in `console.log`
      Property: [/*'key',*/ 'value'] // `key` in `{key: 'value'}`
    },

    enter: function (node) {
      // If the node is an identifier, run our `enter` on it
      if (node.type === 'Identifier' && params.enter) {
        params.enter(node);
      }
    },
    exit: function (node) {
      // If the node is an identifier, run our `exit` on it
      if (node.type === 'Identifier' && params.exit) {
        params.exit(node);
      }
    }
  });
  return ast;
}

// TODO: Consider breaking down ecmaVariableScope into 3 functions

// Define our variable scope function
// DEV: This function goes in 3 parts
//   1. Generate a scope chain (e.g. `FunctionDeclaration` -> `FunctionDeclaration` -> `Program`)
//   2. Resolve variable declarations to their closest scope
//        (e.g. `function hello(world) {}` maps `world` to the scope of `hello`)
//   3. Match all identifiers to their corresponding scope and save `with` related info
//   4. Add scope info to each identifier
//        (e.g. `function a() { var x; function b() { x(); } }` gives `x` the properties recorded against `a`)
function ecmaVariableScope(ast) {
  // Walk entire tree
  var scopeStack = [];
  var scope;
  estraverse.traverse(ast, {
    // Upon entry of a node
    enter: function (node, parent) {
      // Save the scope to our node
      // e.g. {`FunctionDeclaration._nearestScope === Program`}
      // e.g. {`Program._nearestScope === undefined`}
      node._nearestScope = scope;

      // If it is a a program, lexical scope, with statement, or block scope
      var scopeType = getScopeType(node);
      if (scopeType) {
        // Generate a new scope and push it onto our stack
        var oldScope = scope;
        scope = {
          type: scopeType,
          node: node, // reference to node of AST
          parent: oldScope, // reference to parent in scope tree
          identifiers: { // reference to Identifier nodes in AST under our scope
            // name: Identifier
          },
          children: [] // references to children in scope tree
        };
        scopeStack.push(scope);

        // If there was a parent scope, save a child reference
        if (scope.parent) {
          scope.parent.children.push(scope);
        }
      }
    },
    // Upon exiting a node
    leave: function (node, parent) {
      // If it is a program, lexical scope, with statement, or a block scope
      if (getScopeType(node)) {
        // Pop it from the stack
        scopeStack.pop();

        // Update the scope reference
        scope = scopeStack[scopeStack.length - 1];
      }
    }
  });

  // Walk entire tree
  estraverse.traverse(ast, {
    enter: function (node, parent) {
      // If the item is a variable declaration
      var identifiers;
      var scopeType;
      switch(node.type) {
        // Lexical/block scope
        // If it is a variable declaration (e.g. `var name;`, `let name;`)
        // DEV: This also covers `for/for in/for of` loops since they require a `let`
        case 'VariableDeclaration':
          // Find all of the identifiers via the declarators
          var declarators = node.declarations;
          // TODO: Consider using _.pluck
          identifiers = declarators.map(function getIdentifier (declarator) {
            return declarator.id;
          });
          // DEV: For reference, `const` is lexical
          //   if (true) { const hai = true; } console.log(hai);
          scopeType = node.kind === 'let' ? SCOPE_INFO_TYPES.BLOCK : SCOPE_INFO_TYPES.LEXICAL;
          resolveScope(node, identifiers, scopeType);
          break;

        // Lexical scopes
        // `function name(params) {}`
        case 'FunctionDeclaration':
        // `[].map(function name (params) {})`
        case 'FunctionExpression':
        // `[].map(name => 1);
        case 'ArrowFunctionExpression':
          // DEV: This covers both the function name and its parameters
          // Resolve the scope of the fn name separately from its params (different scopes)
          // DEV: Allow no name (e.g. `[].map(function () {})`
          if (node.id) {
            resolveScope(node, [node.id], SCOPE_INFO_TYPES.LEXICAL);
          }

          // Resolve scope for the fn parameters (will resolve to fn)
          if (node.params[0]) {
            resolveScope(node.params[0], node.params, SCOPE_INFO_TYPES.LEXICAL);
          }
          break;

        // Block scopes
        // `try { /* ... */ } catch (name) { /* ... */ }
        case 'CatchClause':
          resolveScope(node.param, [node.param], SCOPE_INFO_TYPES.BLOCK);
          break;
        // `var a = [1 for (value of ['hello'])];`
        case 'ComprehensionBlock':
          resolveScope(node.left, [node.left], SCOPE_INFO_TYPES.BLOCK);
          break;
      }
    }
  });

  // For each variable node
  walkVariables(ast, {
    enter: function (node, parent) {
      // Walk up the scope chain until we find our containing scope
      var scope = node._nearestScope;
      var insideWith = SCOPE_INFO_INSIDE_WITH.NO;
      var usedInAWith = SCOPE_INFO_USED_IN_A_WITH.NO;
      var declaringIdentifier;
      while (scope) {
        // If we are passing through a `with`, save the info
        if (scope.type === SCOPE_TYPES.WITH) {
          insideWith = SCOPE_INFO_INSIDE_WITH.YES;
          usedInAWith = SCOPE_INFO_USED_IN_A_WITH.YES;
        }

        // If it contains our variable, stop looping
        declaringIdentifier = scope.identifiers[node.name];
        if (declaringIdentifier !== undefined) {
          break;
        }

        // Otherwise, move to the parent scope
        scope = scope.parent;
      }

      // Mark the declaringIdentifier with `usedInAWith` info
      if (declaringIdentifier) {
        declaringIdentifier._usedInAWith = usedInAWith;
      }

      // Save the scope and `insideWith` info to our variable
      node_insideWith = insideWith;
      node.scope = scope;
    }
  });

  // For each variable node
  walkVariables(ast, {
    enter: function (node, parent) {
      // Pick the scope for the node
      var scope = node.scope;

      // Define the default info
      var info = {
        declared: SCOPE_INFO_DECLARED.NO,
        insideWith: node._insideWith,
        usedInAWith: SCOPE_INFO_USED_IN_A_WITH.NO,
        topLevel: SCOPE_INFO_TOP_LEVEL.NO,
        type: SCOPE_INFO_TYPES.UNDECLARED
      };

      // If there is a scope, use its `usedInAWith` info
      if (scope) {
        var declaringIdentifier = scope.identifier[node.name];
        if (declaringIdentifier) {
          info.usedInAWith = declaringIdentifier._usedInAWith;
        }
      }

      // If we didn't find a scope or we are at the top level, mark info as top level
      if (scope === undefined || scope.node.type === 'Program') {
        info.topLevel = SCOPE_INFO_TOP_LEVEL.YES;
      }

      // If we hit an identifier, save the type
      if (identifier) {
        info.type = identifier._scopeType;
      }

      // If we saw a `with`, mark `declared` as unknown
      // Attribution: https://github.com/dlsym/segmentation_fuuuu/blob/0bd35fbe215c45fda75e47c3d63df7c4be76b2c6/README.md
      // WITH(OBJ)  ________
      //         __/     /  \  FFFFFFF
      //        /   \   /__  \ FFFFFFF
      //       /  ___\  | (|  \  FFFFFF
      //       | |)  |  \__/   \  FFFUU
      //       | \ _ / ____     |  UUUU
      //       |      / VVV\_   |  UUUU
      //       |     /   ~   \  |  UUUU
      //        \   /__M_M___/  |  UUUU
      //         \_____________/   UUUU-
      if (info.insideWith === true) {
        info.declared = SCOPE_INFO_DECLARED.UNKNOWN;
      // Otherwise, if we didn't find a scope, marked it as undeclared
      } else if (info.type === SCOPE_INFO_TYPES.UNDECLARED) {
        info.declared = SCOPE_INFO_DECLARED.NO;
      // Otherwise, mark this identifier as declared
      } else {
        info.declared = SCOPE_INFO_DECLARED.YES;
      }

      // Save our info to the identifier
      node.scopeInfo = info;
    }
  });

  // Return our marked up AST
  return ast;
}

// Save constants to our export
ecmaVariableScope.SCOPE_INFO_DECLARED = SCOPE_INFO_DECLARED;
ecmaVariableScope.SCOPE_INFO_INSIDE_WITH = SCOPE_INFO_INSIDE_WITH;
ecmaVariableScope.SCOPE_INFO_TOP_LEVEL = SCOPE_INFO_TOP_LEVEL;
ecmaVariableScope.SCOPE_INFO_TYPES = SCOPE_INFO_TYPES;
ecmaVariableScope.SCOPE_TYPES = SCOPE_TYPES;

// Export our funtion
module.exports = ecmaVariableScope;
<commit_msg>Updated declaring identifier<commit_after>// Load in our dependencies
var assert = require('assert');
var ecmaScopes = require('ecma-scopes');
var estraverse = require('estraverse');

// Define constants
var SCOPE_INFO_DECLARED = {
  YES: true,
  UNKNOWN: 'unknown',
  NO: false
};
var SCOPE_INFO_INSIDE_WITH = {
  YES: true,
  NO: false
};
var SCOPE_INFO_USED_IN_A_WITH = {
  YES: true,
  NO: false
};
var SCOPE_INFO_TOP_LEVEL = {
  YES: true,
  NO: false
};
var SCOPE_INFO_TYPES = {
  LEXICAL: 'lexical',
  BLOCK: 'block',
  UNDECLARED: 'undeclared'
};
var SCOPE_TYPES = {
  LEXICAL: 'lexical',
  WITH: 'with',
  BLOCK: 'block'
};

// Define helper to check a new scope
function getScopeType(node) {
  if (node.type === 'Program') {
    // DEV: We mark `program` as `lexical` because it is easier to set/compare against
    return SCOPE_TYPES.LEXICAL;
  } else if (ecmaScopes.lexical.indexOf(node.type) !== -1) {
    return SCOPE_TYPES.LEXICAL;
  } else if (node.type === 'WithStatement') {
    return SCOPE_TYPES.WITH;
  } else if (ecmaScopes.block.indexOf(node.type) !== -1) {
    return SCOPE_TYPES.BLOCK;
  } else {
    return null;
  }
}

// TODO: Move to an object or reduce the parameters. The count is too damn high
// DEV: We specify `baseNode` because it should be where we resolve the nearest scope from which may not be an identifier
function resolveScope(baseNode, identifiers, scopeType) {
  // If we resolved identifiers
  // DEV: This assert is to make it faster for developers to debug
  assert(Array.isArray(identifiers), 'Identifier resolution for baseNode "' + baseNode.type + '" did not result in an array');

  if (identifiers.length) {
    // Walk up the nearest scopes until we find one that matches our type
    // DEV: Use `scopeTypes` since a block scope can hit `Program` which is lexical
    var scope = baseNode._nearestScope;
    var scopeTypes = scopeType === SCOPE_INFO_TYPES.LEXICAL ? [SCOPE_INFO_TYPES.LEXICAL] : [SCOPE_INFO_TYPES.LEXICAL, SCOPE_INFO_TYPES.BLOCK];
    while (scope) {
      // If the scope type matches, stop
      if (scopeTypes.indexOf(scope.type) !== -1) {
        break;
      }

      // Look at the scope's parent
      scope = scope.parent;
    }

    // Expand all identifiers (e.g. object patterns to variables, `var {world}` -> `world`)
    var expandedIdentifiers = [];
    identifiers.forEach(function expandIdentifier (identifier) {
      // If it is an object pattern, expand it and save each key/identifier
      if (identifier.type === 'ObjectPattern') {
        identifier.properties.forEach(function extractObjectIdentifier (property) {
          expandedIdentifiers.push(property.key);
        });
      // Otherwise if it is an array pattern, expand it and save each identifier
      } else if (identifier.type === 'ArrayPattern') {
        identifier.elements.forEach(function extractArrayIdentifier (element) {
          expandedIdentifiers.push(element);
        });
      // Otherwise, save the original identifier
      } else {
        expandedIdentifiers.push(identifier);
      }
    });

    // Save declaration info on the scope and scope type to the identifier
    expandedIdentifiers.forEach(function saveIdentifier (identifier) {
      scope.identifiers[identifier.name] = identifier;
      identifier._scopeType = scopeType;
    });
  }
}

function walkVariables(ast, params) {
  // Walk entire tree
  estraverse.traverse(ast, {
    // Skip over undesired identifiers (e.g. `log` in `console.log`)
    // https://github.com/Constellation/estraverse/blob/1.7.0/estraverse.js#L222-L288
    keys: {
      BreakStatement: [/*'label'*/], // `loop1` in `break loop1;`
      ContinueStatement: [/*'label'*/], // `loop1` in `continue loop1;`
      LabeledStatement: [/*'label',*/ 'body'], // `loop1` in `loop1: while (true) {}`
      MemberExpression: ['object'/*, 'property'*/], // `log` in `console.log`
      Property: [/*'key',*/ 'value'] // `key` in `{key: 'value'}`
    },

    enter: function (node) {
      // If the node is an identifier, run our `enter` on it
      if (node.type === 'Identifier' && params.enter) {
        params.enter(node);
      }
    },
    exit: function (node) {
      // If the node is an identifier, run our `exit` on it
      if (node.type === 'Identifier' && params.exit) {
        params.exit(node);
      }
    }
  });
  return ast;
}

// TODO: Consider breaking down ecmaVariableScope into 3 functions

// Define our variable scope function
// DEV: This function goes in 3 parts
//   1. Generate a scope chain (e.g. `FunctionDeclaration` -> `FunctionDeclaration` -> `Program`)
//   2. Resolve variable declarations to their closest scope
//        (e.g. `function hello(world) {}` maps `world` to the scope of `hello`)
//   3. Match all identifiers to their corresponding scope and save `with` related info
//   4. Add scope info to each identifier
//        (e.g. `function a() { var x; function b() { x(); } }` gives `x` the properties recorded against `a`)
function ecmaVariableScope(ast) {
  // Walk entire tree
  var scopeStack = [];
  var scope;
  estraverse.traverse(ast, {
    // Upon entry of a node
    enter: function (node, parent) {
      // Save the scope to our node
      // e.g. {`FunctionDeclaration._nearestScope === Program`}
      // e.g. {`Program._nearestScope === undefined`}
      node._nearestScope = scope;

      // If it is a a program, lexical scope, with statement, or block scope
      var scopeType = getScopeType(node);
      if (scopeType) {
        // Generate a new scope and push it onto our stack
        var oldScope = scope;
        scope = {
          type: scopeType,
          node: node, // reference to node of AST
          parent: oldScope, // reference to parent in scope tree
          identifiers: { // reference to Identifier nodes in AST under our scope
            // name: Identifier
          },
          children: [] // references to children in scope tree
        };
        scopeStack.push(scope);

        // If there was a parent scope, save a child reference
        if (scope.parent) {
          scope.parent.children.push(scope);
        }
      }
    },
    // Upon exiting a node
    leave: function (node, parent) {
      // If it is a program, lexical scope, with statement, or a block scope
      if (getScopeType(node)) {
        // Pop it from the stack
        scopeStack.pop();

        // Update the scope reference
        scope = scopeStack[scopeStack.length - 1];
      }
    }
  });

  // Walk entire tree
  estraverse.traverse(ast, {
    enter: function (node, parent) {
      // If the item is a variable declaration
      var identifiers;
      var scopeType;
      switch(node.type) {
        // Lexical/block scope
        // If it is a variable declaration (e.g. `var name;`, `let name;`)
        // DEV: This also covers `for/for in/for of` loops since they require a `let`
        case 'VariableDeclaration':
          // Find all of the identifiers via the declarators
          var declarators = node.declarations;
          // TODO: Consider using _.pluck
          identifiers = declarators.map(function getIdentifier (declarator) {
            return declarator.id;
          });
          // DEV: For reference, `const` is lexical
          //   if (true) { const hai = true; } console.log(hai);
          scopeType = node.kind === 'let' ? SCOPE_INFO_TYPES.BLOCK : SCOPE_INFO_TYPES.LEXICAL;
          resolveScope(node, identifiers, scopeType);
          break;

        // Lexical scopes
        // `function name(params) {}`
        case 'FunctionDeclaration':
        // `[].map(function name (params) {})`
        case 'FunctionExpression':
        // `[].map(name => 1);
        case 'ArrowFunctionExpression':
          // DEV: This covers both the function name and its parameters
          // Resolve the scope of the fn name separately from its params (different scopes)
          // DEV: Allow no name (e.g. `[].map(function () {})`
          if (node.id) {
            resolveScope(node, [node.id], SCOPE_INFO_TYPES.LEXICAL);
          }

          // Resolve scope for the fn parameters (will resolve to fn)
          if (node.params[0]) {
            resolveScope(node.params[0], node.params, SCOPE_INFO_TYPES.LEXICAL);
          }
          break;

        // Block scopes
        // `try { /* ... */ } catch (name) { /* ... */ }
        case 'CatchClause':
          resolveScope(node.param, [node.param], SCOPE_INFO_TYPES.BLOCK);
          break;
        // `var a = [1 for (value of ['hello'])];`
        case 'ComprehensionBlock':
          resolveScope(node.left, [node.left], SCOPE_INFO_TYPES.BLOCK);
          break;
      }
    }
  });

  // For each variable node
  walkVariables(ast, {
    enter: function (node, parent) {
      // Walk up the scope chain until we find our containing scope
      var scope = node._nearestScope;
      var insideWith = SCOPE_INFO_INSIDE_WITH.NO;
      var usedInAWith = SCOPE_INFO_USED_IN_A_WITH.NO;
      var declaringIdentifier;
      while (scope) {
        // If we are passing through a `with`, save the info
        if (scope.type === SCOPE_TYPES.WITH) {
          insideWith = SCOPE_INFO_INSIDE_WITH.YES;
          usedInAWith = SCOPE_INFO_USED_IN_A_WITH.YES;
        }

        // If it contains our variable, stop looping
        declaringIdentifier = scope.identifiers[node.name];
        if (declaringIdentifier !== undefined) {
          break;
        }

        // Otherwise, move to the parent scope
        scope = scope.parent;
      }

      // Mark the declaringIdentifier with `usedInAWith` info
      if (declaringIdentifier) {
        declaringIdentifier._usedInAWith = usedInAWith;
      }

      // Save the scope and `insideWith` info to our variable
      node_insideWith = insideWith;
      node.scope = scope;
    }
  });

  // For each variable node
  walkVariables(ast, {
    enter: function (node, parent) {
      // Pick the scope for the node
      var scope = node.scope;

      // Define the default info
      var info = {
        declared: SCOPE_INFO_DECLARED.NO,
        insideWith: node._insideWith,
        usedInAWith: SCOPE_INFO_USED_IN_A_WITH.NO,
        topLevel: SCOPE_INFO_TOP_LEVEL.NO,
        type: SCOPE_INFO_TYPES.UNDECLARED
      };

      // If there is a scope, find its declaring identifier
      var declaringIdentifier;
      if (scope) {
        declaringIdentifier = scope.identifier[node.name];
      }

      if (scope === undefined || scope.node.type === 'Program') {
        info.topLevel = SCOPE_INFO_TOP_LEVEL.YES;
      }

      // If we hit an identifier, save the type and used in a with info
      if (declaringIdentifier) {
        info.usedInAWith = declaringIdentifier._usedInAWith;
        info.type = declaringIdentifier._scopeType;
      }

      // TODO: Deprecate this info
      // If we saw a `with`, mark `declared` as unknown
      // Attribution: https://github.com/dlsym/segmentation_fuuuu/blob/0bd35fbe215c45fda75e47c3d63df7c4be76b2c6/README.md
      // WITH(OBJ)  ________
      //         __/     /  \  FFFFFFF
      //        /   \   /__  \ FFFFFFF
      //       /  ___\  | (|  \  FFFFFF
      //       | |)  |  \__/   \  FFFUU
      //       | \ _ / ____     |  UUUU
      //       |      / VVV\_   |  UUUU
      //       |     /   ~   \  |  UUUU
      //        \   /__M_M___/  |  UUUU
      //         \_____________/   UUUU-
      if (info.insideWith === true) {
        info.declared = SCOPE_INFO_DECLARED.UNKNOWN;
      // Otherwise, if we didn't find a scope, marked it as undeclared
      } else if (info.type === SCOPE_INFO_TYPES.UNDECLARED) {
        info.declared = SCOPE_INFO_DECLARED.NO;
      // Otherwise, mark this identifier as declared
      } else {
        info.declared = SCOPE_INFO_DECLARED.YES;
      }

      // Save our info to the identifier
      node.scopeInfo = info;
    }
  });

  // Return our marked up AST
  return ast;
}

// Save constants to our export
ecmaVariableScope.SCOPE_INFO_DECLARED = SCOPE_INFO_DECLARED;
ecmaVariableScope.SCOPE_INFO_INSIDE_WITH = SCOPE_INFO_INSIDE_WITH;
ecmaVariableScope.SCOPE_INFO_TOP_LEVEL = SCOPE_INFO_TOP_LEVEL;
ecmaVariableScope.SCOPE_INFO_TYPES = SCOPE_INFO_TYPES;
ecmaVariableScope.SCOPE_TYPES = SCOPE_TYPES;

// Export our funtion
module.exports = ecmaVariableScope;
<|endoftext|>


<commit_before>!function(exports) {
  'use strict';

  var TARGET_FPS = 60,
      TARGET_FRAMETIME = 1000 / TARGET_FPS,
      MIN_FRAMETIME = 10;

  function getTimeout(runFn) {
    var prevTime,
        nextTickTime = TARGET_FRAMETIME,
        stopped = false;

    function onTick() {
      if(stopped) return;

      var timestamp = (new Date).valueOf(),
          delta = timestamp - prevTime,
          offset = delta - nextTickTime;

      prevTime = timestamp;
      nextTickTime = TARGET_FRAMETIME - offset;
      if(nextTickTime < MIN_FRAMETIME) nextTickTime = MIN_FRAMETIME;

      runFn(delta);
      keepGoing(nextTickTime);
    }

    function keepGoing(nextTickTime) {
      setTimeout(onTick, nextTickTime);
    }

    return {
      start: function() {
        stopped = false;
        prevTime = (new Date).valueOf();
        keepGoing(TARGET_FRAMETIME);
      },
      stop: function() { stopped = true; }
    };
  }

  exports.loop = {
    timeout: getTimeout
  };

}(seine);
<commit_msg>Use requestAnimationFrame instead of setTimeout.<commit_after>!function(window, exports) {
  'use strict';

  var TARGET_FPS = 60,
      TARGET_FRAMETIME = 1000 / TARGET_FPS,
      MIN_FRAMETIME = 10;

  var rAF = window.requestAnimationFrame ||
            window.webkitRequestAnimationFrame ||
            window.mozRequestAnimationFrame ||
            window.msRequestAnimationFrame ||
            window.oRequestAnimationFrame ||
            fallback;

  // TODO: Use the nextTickTime calculated in onTick() to set the timeout,
  // rather than using raw setTimeout calls.
  function fallback(callback) {
    setTimeout(callback, TARGET_FRAMETIME);
  }

  function getTimeout(runFn) {
    var prevTime,
        nextTickTime = TARGET_FRAMETIME,
        stopped = false;

    function onTick() {
      if(stopped) return;

      var timestamp = (new Date).valueOf(),
          delta = timestamp - prevTime,
          offset = delta - nextTickTime;

      prevTime = timestamp;
      nextTickTime = TARGET_FRAMETIME - offset;
      if(nextTickTime < MIN_FRAMETIME) nextTickTime = MIN_FRAMETIME;

      runFn(delta);
      rAF(onTick);
    }

    return {
      start: function() {
        stopped = false;
        prevTime = (new Date).valueOf();
        rAF(onTick);
      },
      stop: function() { stopped = true; }
    };
  }

  exports.loop = {
    timeout: getTimeout
  };

}(window, seine);
<|endoftext|>


<commit_before>/**
 * @author mrdoob / http://mrdoob.com/
 * @author schteppe / https://github.com/schteppe
 */
 var PointerLockControls = function ( camera, cannonBody, alpha) {
	var alpha = alpha || 0.5;
    var eyeYPos = 10; // eyes are 2 meters above the ground
    var velocityFactor = 0.1;
    var jumpVelocity = 5 * alpha;
    var runDelta = 1;
    var goDelta = 2 * runDelta;
    var scope = this;

    var pitchObject = new THREE.Object3D();
    pitchObject.add( camera );

    var yawObject = new THREE.Object3D();
    yawObject.position.y = 2;
    yawObject.add( pitchObject );

    var quat = new THREE.Quaternion();

    var moveForward = false;
    var moveBackward = false;
    var moveLeft = false;
    var moveRight = false;

    var canJump = false;

    var contactNormal = new CANNON.Vec3(); // Normal in the contact, pointing *out* of whatever the player touched
    var upAxis = new CANNON.Vec3(0,1,0);
    cannonBody.addEventListener("collide",function(e){
        var contact = e.contact;

        // contact.bi and contact.bj are the colliding bodies, and contact.ni is the collision normal.
        // We do not yet know which one is which! Let's check.
        if(contact.bi.id == cannonBody.id)  // bi is the player body, flip the contact normal
            contact.ni.negate(contactNormal);
        else
            contactNormal.copy(contact.ni); // bi is something else. Keep the normal as it is

        // If contactNormal.dot(upAxis) is between 0 and 1, we know that the contact normal is somewhat in the up direction.
        if(contactNormal.dot(upAxis) > 0.5) // Use a "good" threshold value between 0 and 1 here!
            canJump = true;
    });

    var velocity = cannonBody.velocity;

    var PI_2 = Math.PI / 2;

    var onMouseMove = function ( event ) {

        if ( scope.enabled === false ) return;

        var movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
        var movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0;

        yawObject.rotation.y -= movementX * 0.002;
        pitchObject.rotation.x -= movementY * 0.002;

        pitchObject.rotation.x = Math.max( - PI_2, Math.min( PI_2, pitchObject.rotation.x ) );
    };

    var onKeyDown = function ( event ) {

        switch ( event.keyCode ) {

            case 38: // up
            case 87: // w
                moveForward = true;
                break;

            case 37: // left
            case 65: // a
                moveLeft = true; break;

            case 40: // down
            case 83: // s
                moveBackward = true;
                break;

            case 39: // right
            case 68: // d
                moveRight = true;
                break;

            case 32: // space
                if ( canJump === true ){
                    velocity.y = jumpVelocity;
                }
                canJump = false;
                break;

            case 15: // shift
                if ( canJump === true ){
                    runDelta = 3;
                }
                break;
        }

    };

    var onKeyUp = function ( event ) {

        switch( event.keyCode ) {

            case 38: // up
            case 87: // w
                moveForward = false;
                break;

            case 37: // left
            case 65: // a
                moveLeft = false;
                break;

            case 40: // down
            case 83: // a
                moveBackward = false;
                break;

            case 39: // right
            case 68: // d
                moveRight = false;
                break;

        }

    };

    document.addEventListener( 'mousemove', onMouseMove, false );
    document.addEventListener( 'keydown', onKeyDown, false );
    document.addEventListener( 'keyup', onKeyUp, false );

    this.enabled = false;

    this.getObject = function () {
        return yawObject;
    };

    this.getDirection = function(targetVec){
        targetVec.set(0,0,-1);
        quat.multiplyVector3(targetVec);
    }

    // Moves the camera to the Cannon.js object position and adds velocity to the object if the run key is down
    var inputVelocity = new THREE.Vector3();
    var euler = new THREE.Euler();
    this.update = function ( delta ) {

        if ( scope.enabled === false ) return;

        delta *= 0.03;

        inputVelocity.set(0,0,0);

        if ( moveForward ){
            inputVelocity.z = -velocityFactor * delta * alpha * goDelta;
        }
        if ( moveBackward ){
            inputVelocity.z = velocityFactor * delta * alpha * goDelta;
        }

        if ( moveLeft ){
            inputVelocity.x = -velocityFactor * delta * alpha * goDelta;
        }
        if ( moveRight ){
            inputVelocity.x = velocityFactor * delta * alpha * goDelta;
        }

        // Convert velocity to world coordinates
        euler.x = pitchObject.rotation.x;
        euler.y = yawObject.rotation.y;
        euler.order = "XYZ";
        quat.setFromEuler(euler);
        inputVelocity.applyQuaternion(quat);
        //quat.multiplyVector3(inputVelocity);

        // Add to the object
        velocity.x += inputVelocity.x;
        velocity.z += inputVelocity.z;

        yawObject.position.copy(cannonBody.position);
    };
};
<commit_msg>*OBJ* event.webkitMovement => *OBJ* event.movement (update)<commit_after>/**
 * @author mrdoob / http://mrdoob.com/
 * @author schteppe / https://github.com/schteppe
 */
 var PointerLockControls = function ( camera, cannonBody, alpha) {
	var alpha = alpha || 0.5;
    var eyeYPos = 10; // eyes are 2 meters above the ground
    var velocityFactor = 0.1;
    var jumpVelocity = 5 * alpha;
    var runDelta = 1;
    var goDelta = 2 * runDelta;
    var scope = this;

    var pitchObject = new THREE.Object3D();
    pitchObject.add( camera );

    var yawObject = new THREE.Object3D();
    yawObject.position.y = 2;
    yawObject.add( pitchObject );

    var quat = new THREE.Quaternion();

    var moveForward = false;
    var moveBackward = false;
    var moveLeft = false;
    var moveRight = false;

    var canJump = false;

    var contactNormal = new CANNON.Vec3(); // Normal in the contact, pointing *out* of whatever the player touched
    var upAxis = new CANNON.Vec3(0,1,0);
    cannonBody.addEventListener("collide",function(e){
        var contact = e.contact;

        // contact.bi and contact.bj are the colliding bodies, and contact.ni is the collision normal.
        // We do not yet know which one is which! Let's check.
        if(contact.bi.id == cannonBody.id)  // bi is the player body, flip the contact normal
            contact.ni.negate(contactNormal);
        else
            contactNormal.copy(contact.ni); // bi is something else. Keep the normal as it is

        // If contactNormal.dot(upAxis) is between 0 and 1, we know that the contact normal is somewhat in the up direction.
        if(contactNormal.dot(upAxis) > 0.5) // Use a "good" threshold value between 0 and 1 here!
            canJump = true;
    });

    var velocity = cannonBody.velocity;

    var PI_2 = Math.PI / 2;

    var onMouseMove = function ( event ) {

        if ( scope.enabled === false ) return;

        var movementX = event.movementX || event.mozMovementX || 0;
        var movementY = event.movementY || event.mozMovementY || 0;

        yawObject.rotation.y -= movementX * 0.002;
        pitchObject.rotation.x -= movementY * 0.002;

        pitchObject.rotation.x = Math.max( - PI_2, Math.min( PI_2, pitchObject.rotation.x ) );
    };

    var onKeyDown = function ( event ) {

        switch ( event.keyCode ) {

            case 38: // up
            case 87: // w
                moveForward = true;
                break;

            case 37: // left
            case 65: // a
                moveLeft = true; break;

            case 40: // down
            case 83: // s
                moveBackward = true;
                break;

            case 39: // right
            case 68: // d
                moveRight = true;
                break;

            case 32: // space
                if ( canJump === true ){
                    velocity.y = jumpVelocity;
                }
                canJump = false;
                break;

            case 15: // shift
                if ( canJump === true ){
                    runDelta = 3;
                }
                break;
        }

    };

    var onKeyUp = function ( event ) {

        switch( event.keyCode ) {

            case 38: // up
            case 87: // w
                moveForward = false;
                break;

            case 37: // left
            case 65: // a
                moveLeft = false;
                break;

            case 40: // down
            case 83: // a
                moveBackward = false;
                break;

            case 39: // right
            case 68: // d
                moveRight = false;
                break;

        }

    };

    document.addEventListener( 'mousemove', onMouseMove, false );
    document.addEventListener( 'keydown', onKeyDown, false );
    document.addEventListener( 'keyup', onKeyUp, false );

    this.enabled = false;

    this.getObject = function () {
        return yawObject;
    };

    this.getDirection = function(targetVec){
        targetVec.set(0,0,-1);
        quat.multiplyVector3(targetVec);
    }

    // Moves the camera to the Cannon.js object position and adds velocity to the object if the run key is down
    var inputVelocity = new THREE.Vector3();
    var euler = new THREE.Euler();
    this.update = function ( delta ) {

        if ( scope.enabled === false ) return;

        delta *= 0.03;

        inputVelocity.set(0,0,0);

        if ( moveForward ){
            inputVelocity.z = -velocityFactor * delta * alpha * goDelta;
        }
        if ( moveBackward ){
            inputVelocity.z = velocityFactor * delta * alpha * goDelta;
        }

        if ( moveLeft ){
            inputVelocity.x = -velocityFactor * delta * alpha * goDelta;
        }
        if ( moveRight ){
            inputVelocity.x = velocityFactor * delta * alpha * goDelta;
        }

        // Convert velocity to world coordinates
        euler.x = pitchObject.rotation.x;
        euler.y = yawObject.rotation.y;
        euler.order = "XYZ";
        quat.setFromEuler(euler);
        inputVelocity.applyQuaternion(quat);
        //quat.multiplyVector3(inputVelocity);

        // Add to the object
        velocity.x += inputVelocity.x;
        velocity.z += inputVelocity.z;

        yawObject.position.copy(cannonBody.position);
    };
};
<|endoftext|>


<commit_before>var fs = require('fs'),
    path = require('path'),
    flatiron = require('../../flatiron'),
    common = flatiron.common,
    app = flatiron.app;

module.exports = function create(name, type, callback) {
  type = type || 'http';

  var root = path.join(process.cwd(), name),
      scaffold = path.join(__dirname, '..', '..', '..', 'scaffolds', type),
      info = {};

  if (!path.existsSync(scaffold)) {
    app.log.error('Scaffold named ' + type.yellow + ' does not exist');
    return callback();
  }

  //
  // Prompts user for details about their app to put in `package.json`.
  //
  function prompt (next) {
    var fields = ['name', 'author', 'description', 'homepage'];
    app.prompt.override = {name: name};
    app.prompt.start();
    app.prompt.addProperties(info, fields, next);
  }

  //
  // Creates directories specified in `/scaffolds/:type/directories.json`.
  //
  function createDirs(next) {
    var dirs = common.directories.normalize(
      common.mixin({}, flatiron.constants.DIRECTORIES, { '#ROOT': root }),
      JSON.parse(fs.readFileSync(path.join(scaffold, 'directories.json'), 'utf8'))
    );

    Object.keys(dirs).forEach(function (name) {
      app.log.info('Creating directory ' + name.grey);
    });

    common.directories.create(dirs, next);
  }

  //
  // Creates files specified in `/scaffolds/:type/files.json`.
  //
  function createFiles(next) {
    var files = common.directories.normalize(
      common.mixin({}, flatiron.constants.DIRECTORIES, { '#ROOT': root }),
      JSON.parse(fs.readFileSync(path.join(scaffold, 'files.json'), 'utf8'))
    );

    function copyFile(file, nextFile) {
      app.log.info('Writing file ' + file.grey);
      common.cpr(path.join(scaffold, file), files[file], nextFile);
    }

    common.async.mapSeries(Object.keys(files), copyFile, next);
  }

  //
  // Creates a templated package.json from `/scaffolds/:type/package.json`.
  //
  function createPackage(next) {
    var pkg = JSON.parse(fs.readFileSync(path.join(scaffold, 'package.json'), 'utf8'));

    pkg.dependencies.flatiron = flatiron.version;

    flatiron.common.mixin(pkg, info);

    app.log.info('Writing ' + 'package.json'.grey);
    fs.writeFile(path.join(root, 'package.json'), JSON.stringify(pkg, null, 2) + '\n', next);
  }

  app.log.info('Creating application ' + (name ? name.magenta : ''));
  app.log.info('Using ' + type.yellow + ' scaffold.');
  common.async.series([
    prompt,
    createDirs,
    createPackage,
    createFiles
  ], function onComplete(next) {
      app.log.info('Application ' + info.name.magenta + ' is now ready');
      callback();
    }
  );
}

module.exports.usage = [
  'Generates a flatiron skeleton application. If no <type>',
  'is specified an HTTP application will be created.',
  '<type> can currently be either cli or http',
  '',
  'create <app-name> <type>',
];
<commit_msg>[fix] fs.existsSync is added for 0.8 compatibility, Fixes #71<commit_after>var fs = require('fs'),
    path = require('path'),
    flatiron = require('../../flatiron'),
    common = flatiron.common,
    app = flatiron.app;

module.exports = function create(name, type, callback) {
  type = type || 'http';

  var existsSync = fs.existsSync || path.existsSync,
      root = path.join(process.cwd(), name),
      scaffold = path.join(__dirname, '..', '..', '..', 'scaffolds', type),
      info = {};

  if (!existsSync(scaffold)) {
    app.log.error('Scaffold named ' + type.yellow + ' does not exist');
    return callback();
  }

  //
  // Prompts user for details about their app to put in `package.json`.
  //
  function prompt (next) {
    var fields = ['name', 'author', 'description', 'homepage'];
    app.prompt.override = {name: name};
    app.prompt.start();
    app.prompt.addProperties(info, fields, next);
  }

  //
  // Creates directories specified in `/scaffolds/:type/directories.json`.
  //
  function createDirs(next) {
    var dirs = common.directories.normalize(
      common.mixin({}, flatiron.constants.DIRECTORIES, { '#ROOT': root }),
      JSON.parse(fs.readFileSync(path.join(scaffold, 'directories.json'), 'utf8'))
    );

    Object.keys(dirs).forEach(function (name) {
      app.log.info('Creating directory ' + name.grey);
    });

    common.directories.create(dirs, next);
  }

  //
  // Creates files specified in `/scaffolds/:type/files.json`.
  //
  function createFiles(next) {
    var files = common.directories.normalize(
      common.mixin({}, flatiron.constants.DIRECTORIES, { '#ROOT': root }),
      JSON.parse(fs.readFileSync(path.join(scaffold, 'files.json'), 'utf8'))
    );

    function copyFile(file, nextFile) {
      app.log.info('Writing file ' + file.grey);
      common.cpr(path.join(scaffold, file), files[file], nextFile);
    }

    common.async.mapSeries(Object.keys(files), copyFile, next);
  }

  //
  // Creates a templated package.json from `/scaffolds/:type/package.json`.
  //
  function createPackage(next) {
    var pkg = JSON.parse(fs.readFileSync(path.join(scaffold, 'package.json'), 'utf8'));

    pkg.dependencies.flatiron = flatiron.version;

    flatiron.common.mixin(pkg, info);

    app.log.info('Writing ' + 'package.json'.grey);
    fs.writeFile(path.join(root, 'package.json'), JSON.stringify(pkg, null, 2) + '\n', next);
  }

  app.log.info('Creating application ' + (name ? name.magenta : ''));
  app.log.info('Using ' + type.yellow + ' scaffold.');
  common.async.series([
    prompt,
    createDirs,
    createPackage,
    createFiles
  ], function onComplete(next) {
      app.log.info('Application ' + info.name.magenta + ' is now ready');
      callback();
    }
  );
}

module.exports.usage = [
  'Generates a flatiron skeleton application. If no <type>',
  'is specified an HTTP application will be created.',
  '<type> can currently be either cli or http',
  '',
  'create <app-name> <type>',
];
<|endoftext|>


<commit_before>/**
 * Resolve the properties of an object in a runtime.
 *
 * Singleton. Every instantiation will return the same instance. Contains
 * no state apart from the caching, which should be shared.
 */

window.cls = window.cls || {};
window.cls.PropertyFinder = function(rt_id) {
  if (window.cls.PropertyFinder.instance) {
    return window.cls.PropertyFinder.instance;
  }
  else {
    window.cls.PropertyFinder.instance = this;
  }

  // this cond is here so we can instanciate the class even without running
  // with scope. Means we can run tests on functions that don't require
  // scope.
  if (window.services)
  {
    this._service = window.services['ecmascript-debugger'];
  }

  this._cache = {};

  /**
   * Figure out the object to which input belongs.
   * foo.bar -> foo
   * window.bleh.meh -> window.bleh
   * phlebotinum -> this
   * phlebotinum. -> phlebotinum
   * foo(bar.bleh -> bar
   * foo[window -> window
   * foo[bar].a -> foo[bar]
   */
  this._find_input_parts = function(input)
  {
    var last_bracket = input.lastIndexOf('[');
    var last_brace = input.lastIndexOf('(');

    last_brace = input.lastIndexOf(')') <= last_brace ? last_brace : -1;
    last_bracket = input.lastIndexOf(']') <= last_bracket ? last_bracket : -1;
    input = input.slice( Math.max(
                  last_brace,
                  last_bracket,
                  input.lastIndexOf('=') ) + 1
                ).replace(/^\s+/, '');

    var last_dot = input.lastIndexOf('.');
    var new_path = '';
    var new_id = '';
    var ret = '';

    if(last_dot > -1)
    {
      new_path = input.slice(0, last_dot);
      new_id = input.slice(last_dot + 1).replace(/^\s+/, '');
    }
    else
    {
      new_id = input;
    }

    return {scope: new_path, identifier: new_id, input: input};
  };

  /**
   * Returns a list of properties that match the input string in the given
   * runtime.
   *
   */
  this.find_props = function(callback, input, frameinfo, context)
  {
    frameinfo = frameinfo ||
    {
      runtime_id: runtimes.getSelectedRuntimeId(),
      thread_id: 0,
      scope_id: runtimes.getRuntime(runtimes.getSelectedRuntimeId()).object_id,
      index: 0,
      scope_list: []
    };

    frameinfo.stopped = Boolean(frameinfo.thread_id);
    var parts = this._find_input_parts(input);
    var props = this._cache_get(parts.scope, frameinfo);

    if (props) {
      props.input = input;
      props.identifier = parts.identifier;
      callback(props);
    }
    else {
      if (parts.scope)
      {
        this._get_subject_id(callback, parts.scope, frameinfo, parts);
      }
      else
      {
        var scopes = [frameinfo.scope_id].extend(frameinfo.scope_list || []);
        this._get_object_props(callback, frameinfo, scopes, parts);
      }
    }
  };

  /**
   * Tell the caching mechanism that it need no longer keep track of data
   * about a particular runtime. Can be hooked up to messages about closed
   * tabs/runtimes
   */
  this.clear_cache = function(rt_id) {
    this._cache = {};
  };

  this._cache_key = function(scope, frameinfo) {
    return "" + scope + "." + frameinfo.runtime_id + "." + frameinfo.thread_id + "." + frameinfo.index;
  };

  this._cache_put = function(result)
  {
    var key = this._cache_key(result.scope, result.frameinfo);
    this._cache[key] = result;
  };

  this._cache_get = function(scope, frameinfo) {
    var key = this._cache_key(scope, frameinfo);
    return this._cache[key];
  };

  this._get_subject_id = function(callback, scope, frameinfo, parts)
  {
    var js = "" + scope;
    var tag = tagManager.set_callback(this, this._got_subject_id,
                                      [callback, frameinfo, parts]);
    this._service.requestEval(
      tag, [frameinfo.runtime_id, frameinfo.thread_id, frameinfo.index, js]
    );
  }

  this._got_subject_id = function(status, msg, callback, frameinfo, parts)
  {
    const EVALRESULT = 0; OBJVALUE = 3, OBJID = 0;
    var objid = null;
    if (msg && msg[EVALRESULT] == "completed" && msg[OBJVALUE])
    {
      objid = msg[OBJVALUE][OBJID];
      this._get_object_props(callback, frameinfo, [objid], parts)
    }
    else
    {
      var ret = {
        props: [],
        scope: parts.scope,
        input: parts.input,
        identifier: parts.identifier,
      };
      callback(ret);
    }
  }

  this._get_object_props = function(callback, frameinfo, objids, parts)
  {
    var tag = tagManager.set_callback(this, this._got_object_props, [callback, parts]);
    this._service.requestExamineObjects(tag, [frameinfo.runtime_id, objids, 1, 0]);
  }

  this._got_object_props = function(status, msg, callback, parts)
  {
    var ret = {
      props: this._parse_prop_list(msg) || [],
      scope: parts.scope,
      input: parts.input,
      identifier: parts.identifier,
    };

    if (ret.props.indexOf("this") == -1)
    {
        ret.props.push("this");
    }

    // fixme: is "arguments" in the props when stopped?
    //this._cache_put(ret);
    callback(ret);
  }

  this._parse_prop_list = function(msg)
  {
    var names = [];
    if (status == 0) {
      const OBJECT_CHAIN_LIST = 0, OBJECT_LIST = 0, PROPERTY_LIST = 1, NAME = 0;
      (msg[OBJECT_CHAIN_LIST] || []).forEach(function(chain){
        var objectlist = chain[OBJECT_LIST] || [];
        objectlist.forEach(function(obj) {
          names.extend((obj[PROPERTY_LIST] || []).map(function(prop) {
            return prop[NAME];
          }));
        });
      });
    }
    return names.unique();
  }

  this.toString = function() {
    return "[PropertyFinder singleton instance]";
  };
};

cls.PropertyFinder.prop_sorter = function(a, b)
{
  a = a.toLowerCase();
  b = b.toLowerCase();

  if (a.isdigit() && b.isdigit())
  {
    return parseInt(a, 10) - parseInt(b, 10);
  }
  else if (a>b)
  {
    return 1;
  }
  else if (a<b)
  {
    return -1;
  }
  return 0;
};
<commit_msg>Makde bash and  work with propertyresolver. Resolves DFL-2214<commit_after>/**
 * Resolve the properties of an object in a runtime.
 *
 * Singleton. Every instantiation will return the same instance. Contains
 * no state apart from the caching, which should be shared.
 */

window.cls = window.cls || {};
window.cls.PropertyFinder = function(rt_id) {
  if (window.cls.PropertyFinder.instance) {
    return window.cls.PropertyFinder.instance;
  }
  else {
    window.cls.PropertyFinder.instance = this;
  }

  // this cond is here so we can instanciate the class even without running
  // with scope. Means we can run tests on functions that don't require
  // scope.
  if (window.services)
  {
    this._service = window.services['ecmascript-debugger'];
  }

  this._cache = {};

  /**
   * Figure out the object to which input belongs.
   * foo.bar -> foo
   * window.bleh.meh -> window.bleh
   * phlebotinum -> this
   * phlebotinum. -> phlebotinum
   * foo(bar.bleh -> bar
   * foo[window -> window
   * foo[bar].a -> foo[bar]
   */
  this._find_input_parts = function(input)
  {
    var last_bracket = input.lastIndexOf('[');
    var last_brace = input.lastIndexOf('(');

    last_brace = input.lastIndexOf(')') <= last_brace ? last_brace : -1;
    last_bracket = input.lastIndexOf(']') <= last_bracket ? last_bracket : -1;
    input = input.slice( Math.max(
                  last_brace,
                  last_bracket,
                  input.lastIndexOf('=') ) + 1
                ).replace(/^\s+/, '');

    var last_dot = input.lastIndexOf('.');
    var new_path = '';
    var new_id = '';
    var ret = '';

    if(last_dot > -1)
    {
      new_path = input.slice(0, last_dot);
      new_id = input.slice(last_dot + 1).replace(/^\s+/, '');
    }
    else
    {
      new_id = input;
    }

    return {scope: new_path, identifier: new_id, input: input};
  };

  /**
   * Returns a list of properties that match the input string in the given
   * runtime.
   *
   */
  this.find_props = function(callback, input, frameinfo, context)
  {
    frameinfo = frameinfo ||
    {
      runtime_id: runtimes.getSelectedRuntimeId(),
      thread_id: 0,
      scope_id: runtimes.getRuntime(runtimes.getSelectedRuntimeId()).object_id,
      index: 0,
      scope_list: []
    };

    frameinfo.stopped = Boolean(frameinfo.thread_id);
    var parts = this._find_input_parts(input);
    var props = this._cache_get(parts.scope, frameinfo);

    if (props)
    {
      props.input = input;
      props.identifier = parts.identifier;
      callback(props);
    }
    else
    {
      if (parts.scope)
      {
        this._get_subject_id(callback, parts.scope, frameinfo, parts, context);
      }
      else
      {
        var scopes = [frameinfo.scope_id].extend(frameinfo.scope_list || []);
        this._get_object_props(callback, frameinfo, scopes, parts);
      }
    }
  };

  /**
   * Tell the caching mechanism that it need no longer keep track of data
   * about a particular runtime. Can be hooked up to messages about closed
   * tabs/runtimes
   */
  this.clear_cache = function(rt_id) {
    this._cache = {};
  };

  this._cache_key = function(scope, frameinfo) {
    return "" + scope + "." + frameinfo.runtime_id + "." + frameinfo.thread_id + "." + frameinfo.index;
  };

  this._cache_put = function(result)
  {
    var key = this._cache_key(result.scope, result.frameinfo);
    this._cache[key] = result;
  };

  this._cache_get = function(scope, frameinfo) {
    var key = this._cache_key(scope, frameinfo);
    return this._cache[key];
  };

  this._get_subject_id = function(callback, scope, frameinfo, parts, context)
  {
    var js = "" + scope;

    var varlist = [];
    if (context)
    {
      for (var key in context)
      {
        varlist.push([key, context[key]]);
      }
    }

    var tag = tagManager.set_callback(this, this._got_subject_id,
                                      [callback, frameinfo, parts]);
    this._service.requestEval(
      tag, [frameinfo.runtime_id, frameinfo.thread_id, frameinfo.index, js, varlist]
    );
  }

  this._got_subject_id = function(status, msg, callback, frameinfo, parts)
  {
    const EVALRESULT = 0; OBJVALUE = 3, OBJID = 0;
    var objid = null;
    if (msg && msg[EVALRESULT] == "completed" && msg[OBJVALUE])
    {
      objid = msg[OBJVALUE][OBJID];
      this._get_object_props(callback, frameinfo, [objid], parts)
    }
    else
    {
      var ret = {
        props: [],
        scope: parts.scope,
        input: parts.input,
        identifier: parts.identifier,
      };
      callback(ret);
    }
  }

  this._get_object_props = function(callback, frameinfo, objids, parts)
  {
    var tag = tagManager.set_callback(this, this._got_object_props, [callback, parts]);
    this._service.requestExamineObjects(tag, [frameinfo.runtime_id, objids, 1, 0]);
  }

  this._got_object_props = function(status, msg, callback, parts)
  {
    var ret = {
      props: this._parse_prop_list(msg) || [],
      scope: parts.scope,
      input: parts.input,
      identifier: parts.identifier,
    };

    if (ret.props.indexOf("this") == -1)
    {
        ret.props.push("this");
    }

    // fixme: is "arguments" in the props when stopped?
    //this._cache_put(ret);
    callback(ret);
  }

  this._parse_prop_list = function(msg)
  {
    var names = [];
    if (status == 0) {
      const OBJECT_CHAIN_LIST = 0, OBJECT_LIST = 0, PROPERTY_LIST = 1, NAME = 0;
      (msg[OBJECT_CHAIN_LIST] || []).forEach(function(chain){
        var objectlist = chain[OBJECT_LIST] || [];
        objectlist.forEach(function(obj) {
          names.extend((obj[PROPERTY_LIST] || []).map(function(prop) {
            return prop[NAME];
          }));
        });
      });
    }
    return names.unique();
  }

  this.toString = function() {
    return "[PropertyFinder singleton instance]";
  };
};

cls.PropertyFinder.prop_sorter = function(a, b)
{
  a = a.toLowerCase();
  b = b.toLowerCase();

  if (a.isdigit() && b.isdigit())
  {
    return parseInt(a, 10) - parseInt(b, 10);
  }
  else if (a>b)
  {
    return 1;
  }
  else if (a<b)
  {
    return -1;
  }
  return 0;
};
<|endoftext|>


<commit_before>import { path } from '../core/path.js';

import { Quat } from '../math/quat.js';
import { Vec3 } from '../math/vec3.js';

import { http, Http } from '../net/http.js';

import { Animation, Key, Node } from '../animation/animation.js';

import { GlbParser } from '../resources/parser/glb-parser.js';

/**
 * @class
 * @name pc.AnimationHandler
 * @implements {pc.ResourceHandler}
 * @classdesc Resource handler used for loading {@link pc.Animation} resources.
 */
class AnimationHandler {
    constructor() {
        this.maxRetries = 0;
    }

    load(url, callback) {
        if (typeof url === 'string') {
            url = {
                load: url,
                original: url
            };
        }

        // we need to specify JSON for blob URLs
        var options = {
            retry: this.maxRetries > 0,
            maxRetries: this.maxRetries
        };

        if (url.load.startsWith('blob:') || url.load.startsWith('data:')) {
            if (path.getExtension(url.original).toLowerCase() === '.glb') {
                options.responseType = Http.ResponseType.ARRAY_BUFFER;
            } else {
                options.responseType = Http.ResponseType.JSON;
            }
        }

        http.get(url.load, options, function (err, response) {
            if (err) {
                callback("Error loading animation resource: " + url.original + " [" + err + "]");
            } else {
                callback(null, response);
            }
        });
    }

    open(url, data) {
        if (path.getExtension(url).toLowerCase() === '.glb') {
            var glb = GlbParser.parse("filename.glb", data, null);
            if (!glb) {
                return null;
            }
            return glb.animations;
        }
        return this["_parseAnimationV" + data.animation.version](data);
    }

    _parseAnimationV3(data) {
        var animData = data.animation;

        var anim = new Animation();
        anim.setName(animData.name);
        anim.duration = animData.duration;

        for (var i = 0; i < animData.nodes.length; i++) {
            var node = new Node();

            var n = animData.nodes[i];
            node._name = n.name;

            for (var j = 0; j < n.keys.length; j++) {
                var k = n.keys[j];

                var t = k.time;
                var p = k.pos;
                var r = k.rot;
                var s = k.scale;
                var pos = new Vec3(p[0], p[1], p[2]);
                var rot = new Quat().setFromEulerAngles(r[0], r[1], r[2]);
                var scl = new Vec3(s[0], s[1], s[2]);

                var key = new Key(t, pos, rot, scl);

                node._keys.push(key);
            }

            anim.addNode(node);
        }

        return anim;
    }

    _parseAnimationV4(data) {
        var animData = data.animation;

        var anim = new Animation();
        anim.setName(animData.name);
        anim.duration = animData.duration;

        for (var i = 0; i < animData.nodes.length; i++) {
            var node = new Node();

            var n = animData.nodes[i];
            node._name = n.name;

            var defPos = n.defaults.p;
            var defRot = n.defaults.r;
            var defScl = n.defaults.s;

            for (var j = 0; j < n.keys.length; j++) {
                var k = n.keys[j];

                var t = k.t;
                var p = defPos ? defPos : k.p;
                var r = defRot ? defRot : k.r;
                var s = defScl ? defScl : k.s;
                var pos = new Vec3(p[0], p[1], p[2]);
                var rot = new Quat().setFromEulerAngles(r[0], r[1], r[2]);
                var scl = new Vec3(s[0], s[1], s[2]);

                var key = new Key(t, pos, rot, scl);

                node._keys.push(key);
            }

            anim.addNode(node);
        }

        return anim;
    }
}

export { AnimationHandler };
<commit_msg>[FIX] Animation#setName to Animation#name (#2710)<commit_after>import { path } from '../core/path.js';

import { Quat } from '../math/quat.js';
import { Vec3 } from '../math/vec3.js';

import { http, Http } from '../net/http.js';

import { Animation, Key, Node } from '../animation/animation.js';

import { GlbParser } from '../resources/parser/glb-parser.js';

/**
 * @class
 * @name pc.AnimationHandler
 * @implements {pc.ResourceHandler}
 * @classdesc Resource handler used for loading {@link pc.Animation} resources.
 */
class AnimationHandler {
    constructor() {
        this.maxRetries = 0;
    }

    load(url, callback) {
        if (typeof url === 'string') {
            url = {
                load: url,
                original: url
            };
        }

        // we need to specify JSON for blob URLs
        var options = {
            retry: this.maxRetries > 0,
            maxRetries: this.maxRetries
        };

        if (url.load.startsWith('blob:') || url.load.startsWith('data:')) {
            if (path.getExtension(url.original).toLowerCase() === '.glb') {
                options.responseType = Http.ResponseType.ARRAY_BUFFER;
            } else {
                options.responseType = Http.ResponseType.JSON;
            }
        }

        http.get(url.load, options, function (err, response) {
            if (err) {
                callback("Error loading animation resource: " + url.original + " [" + err + "]");
            } else {
                callback(null, response);
            }
        });
    }

    open(url, data) {
        if (path.getExtension(url).toLowerCase() === '.glb') {
            var glb = GlbParser.parse("filename.glb", data, null);
            if (!glb) {
                return null;
            }
            return glb.animations;
        }
        return this["_parseAnimationV" + data.animation.version](data);
    }

    _parseAnimationV3(data) {
        var animData = data.animation;

        var anim = new Animation();
        anim.name = animData.name;
        anim.duration = animData.duration;

        for (var i = 0; i < animData.nodes.length; i++) {
            var node = new Node();

            var n = animData.nodes[i];
            node._name = n.name;

            for (var j = 0; j < n.keys.length; j++) {
                var k = n.keys[j];

                var t = k.time;
                var p = k.pos;
                var r = k.rot;
                var s = k.scale;
                var pos = new Vec3(p[0], p[1], p[2]);
                var rot = new Quat().setFromEulerAngles(r[0], r[1], r[2]);
                var scl = new Vec3(s[0], s[1], s[2]);

                var key = new Key(t, pos, rot, scl);

                node._keys.push(key);
            }

            anim.addNode(node);
        }

        return anim;
    }

    _parseAnimationV4(data) {
        var animData = data.animation;

        var anim = new Animation();
        anim.name = animData.name;
        anim.duration = animData.duration;

        for (var i = 0; i < animData.nodes.length; i++) {
            var node = new Node();

            var n = animData.nodes[i];
            node._name = n.name;

            var defPos = n.defaults.p;
            var defRot = n.defaults.r;
            var defScl = n.defaults.s;

            for (var j = 0; j < n.keys.length; j++) {
                var k = n.keys[j];

                var t = k.t;
                var p = defPos ? defPos : k.p;
                var r = defRot ? defRot : k.r;
                var s = defScl ? defScl : k.s;
                var pos = new Vec3(p[0], p[1], p[2]);
                var rot = new Quat().setFromEulerAngles(r[0], r[1], r[2]);
                var scl = new Vec3(s[0], s[1], s[2]);

                var key = new Key(t, pos, rot, scl);

                node._keys.push(key);
            }

            anim.addNode(node);
        }

        return anim;
    }
}

export { AnimationHandler };
<|endoftext|>


<commit_before>/**
 * A generator to generate a piece of transpiled Dafny source code.
 */
export default class Generator {

  settings = {
    tab: '  ',
    newline: '\n'
  };

  /**
   * Generates a piece of transpiled Dafny source code.
   *
   * @param  {Object} parseTree the parse tree to generate source code for
   * @return {String}           the transpiled source code
   */
  generate(parseTree) {
    let code = '';
    for (let include of parseTree.includes) {
      code += `include "${include}"${this.newline()}`;
    }
    if (code !== '') {
      code += this.newline();
    }
    code += `class ${parseTree.name}${this.newline()}`;
    code += `{${this.newline()}`;
    for (let variable of parseTree.variables) {
      code += `${this.tab()}${variable};${this.newline()}`;
    }
    code += this.generateCorrectnessMethods(parseTree);
    code += `}${this.newline()}`;
    return code;
  }

  /**
   * Generates source code for the local and global correctness methods.
   *
   * @private
   * @param  {Object} parseTree the parse tree to generate source code for
   * @return {String}           a string containing the generated source code
   */
  generateCorrectnessMethods(parseTree) {
    let code = '';
    const program = parseTree.program;
    const programComponents = program.programComponents;
    code += this.generateLocalCorrectnessMethods(program, programComponents);
    code += this.generateGlobalCorrectnessMethods(program, programComponents);
    return code;
  }

  /**
   * Generates source code for the local correctness methods.
   *
   * @private
   * @param  {Object}         program           the parallel program
   * @param  {Array<Object>}  programComponents the parallel program components
   * @return {String}                           a string containing the generated source code
   */
  generateLocalCorrectnessMethods(program, programComponents) {
    let code = `${this.newline()}${this.tab()}// Local Correctness${this.newline(2)}`;
    const maxCounter = 2 + programComponents.length;
    for (let counter = 1; counter <= maxCounter; counter++) {
      code += this.generateLocalCorrectnessMethod(program, programComponents, counter, maxCounter);
    }
    return code;
  }

  /**
   * Generates source code for a local correctness method.
   *
   * @private
   * @param  {Object}         program           the parallel program
   * @param  {Array<Object>}  programComponents the parallel program components
   * @param  {Number}         counter           the current counter of the method
   * @param  {Number}         maxCounter        the maximum counter of the methods
   * @return {String}                           a string containing the generated source code
   */
  generateLocalCorrectnessMethod(program, programComponents, counter, maxCounter) {
    const localCorrectnessMethod = {};
    localCorrectnessMethod.name = `LocalCorrectness${counter}`;
    if (counter === 1) {
      localCorrectnessMethod.preconditions = [program.precondition];
      localCorrectnessMethod.postconditions = [];
      for (let programComponent of programComponents) {
        localCorrectnessMethod.postconditions.push(programComponent.precondition);
      }
      localCorrectnessMethod.statements = [];
    } else if (counter < maxCounter) {
      const programComponent = programComponents[counter - 2];
      localCorrectnessMethod.preconditions = [programComponent.precondition];
      localCorrectnessMethod.postconditions = [programComponent.postcondition];
      localCorrectnessMethod.statements = programComponent.body;
    } else {
      localCorrectnessMethod.preconditions = [];
      for (let programComponent of programComponents) {
        localCorrectnessMethod.preconditions.push(programComponent.postcondition);
      }
      localCorrectnessMethod.postconditions = [program.postcondition];
      localCorrectnessMethod.statements = [];
    }
    let code = this.generateMethod(localCorrectnessMethod);
    if (counter !== maxCounter) {
      code += this.newline();
    }
    return code;
  }

  /**
   * Generates source code for the global correctness methods.
   *
   * @private
   * @param  {Object}         program           the parallel program
   * @param  {Array<Object>}  programComponents the parallel program components
   * @return {String}                           a string containing the generated source code
   */
  generateGlobalCorrectnessMethods(program, programComponents) {
    let code = `${this.newline()}${this.tab()}// Global Correctness${this.newline(2)}`;
    let counter = 0;
    for (let programComponent1 of programComponents) {
      for (let programComponent2 of programComponents) {
        if (programComponent1 !== programComponent2) {
          const {
            _code,
            _counter
          } = this.generateGlobalCorrectnessMethod(programComponent1, programComponent2, counter);
          code += _code;
          counter = _counter;
        }
      }
    }
    code = code.slice(0, -this.settings.newline.length);
    return code;
  }

  /**
   * Generates source code for a global correctness method.
   *
   * @private
   * @param  {Object} programComponent1 the first program component
   * @param  {Object} programComponent2 the second program component
   * @param  {Number} counter           the current counter of the method
   * @return {String}                   a string containing the generated source code
   */
  generateGlobalCorrectnessMethod(programComponent1, programComponent2, counter) {
    let code = '';
    for (let assertion1 of programComponent1.assertions) {
      for (let assertion2 of programComponent2.assertions) {
        for (let assertion of [assertion2.precondition, assertion2.postcondition]) {
          code += `${this.tab()}// R: ${programComponent1.name} --- S: ${programComponent2.name}${this.newline()}`;
          const globalCorrectnessMethodPrecondition = {};
          globalCorrectnessMethodPrecondition.name = `GlobalCorrectness${++counter}`;
          globalCorrectnessMethodPrecondition.preconditions = [assertion];
          globalCorrectnessMethodPrecondition.preconditions.push(assertion1.precondition);
          globalCorrectnessMethodPrecondition.postconditions = [assertion];
          globalCorrectnessMethodPrecondition.statements = assertion1.statements;
          code += this.generateMethod(globalCorrectnessMethodPrecondition);
          code += this.newline();
        }
      }
    }
    return {
      _code: code,
      _counter: counter
    };
  }

  /**
   * Generates source code for a method.
   *
   * @private
   * @param  {Object} method the method
   * @return {String}        a string containing the generated source code
   */
  generateMethod(method) {
    let code = this.tab();
    code += `method ${method.name}()${this.newline()}`;
    if (method.statements.length > 0) {
      code += `${this.tab(2)}modifies this;${this.newline()}`;
    }
    for (let precondition of method.preconditions) {
      code += `${this.tab(2)}requires ${precondition}${this.newline()}`;
    }
    for (let postcondition of method.postconditions) {
      code += `${this.tab(2)}ensures ${postcondition}${this.newline()}`;
    }
    if (method.statements.length > 0) {
      code += `${this.tab()}{${this.newline()}`;
      for (let statement of method.statements) {
        code += `${this.tab(2)}${statement}${this.newline()}`;
      }
      code += `${this.tab()}}${this.newline()}`;
    }
    return code;
  }

  /**
   * Returns a specified number of tabs. Default: 1
   *
   * @private
   * @param  {Number} [numTabs = 1] the number of tabs to return
   * @return {String}               a string containing the number of tabs
   */
  tab(numTabs = 1) {
    let tabs = '';
    for (let i = 0; i < numTabs; i++) {
      tabs += this.settings.tab;
    }
    return tabs;
  }

  /**
   * Returns a specified number of newlines. Default: 1
   *
   * @private
   * @param  {Number} [numTabs = 1] the number of newlines to return
   * @return {String}               a string containing the number of newlines
   */
  newline(numNewLines = 1) {
    let newlines = '';
    for (let i = 0; i < numNewLines; i++) {
      newlines += this.settings.newline;
    }
    return newlines;
  }
}
<commit_msg>Always generate a method body, even if it is empty<commit_after>/**
 * A generator to generate a piece of transpiled Dafny source code.
 */
export default class Generator {

  settings = {
    tab: '  ',
    newline: '\n'
  };

  /**
   * Generates a piece of transpiled Dafny source code.
   *
   * @param  {Object} parseTree the parse tree to generate source code for
   * @return {String}           the transpiled source code
   */
  generate(parseTree) {
    let code = '';
    for (let include of parseTree.includes) {
      code += `include "${include}"${this.newline()}`;
    }
    if (code !== '') {
      code += this.newline();
    }
    code += `class ${parseTree.name}${this.newline()}`;
    code += `{${this.newline()}`;
    for (let variable of parseTree.variables) {
      code += `${this.tab()}${variable};${this.newline()}`;
    }
    code += this.generateCorrectnessMethods(parseTree);
    code += `}${this.newline()}`;
    return code;
  }

  /**
   * Generates source code for the local and global correctness methods.
   *
   * @private
   * @param  {Object} parseTree the parse tree to generate source code for
   * @return {String}           a string containing the generated source code
   */
  generateCorrectnessMethods(parseTree) {
    let code = '';
    const program = parseTree.program;
    const programComponents = program.programComponents;
    code += this.generateLocalCorrectnessMethods(program, programComponents);
    code += this.generateGlobalCorrectnessMethods(program, programComponents);
    return code;
  }

  /**
   * Generates source code for the local correctness methods.
   *
   * @private
   * @param  {Object}         program           the parallel program
   * @param  {Array<Object>}  programComponents the parallel program components
   * @return {String}                           a string containing the generated source code
   */
  generateLocalCorrectnessMethods(program, programComponents) {
    let code = `${this.newline()}${this.tab()}// Local Correctness${this.newline(2)}`;
    const maxCounter = 2 + programComponents.length;
    for (let counter = 1; counter <= maxCounter; counter++) {
      code += this.generateLocalCorrectnessMethod(program, programComponents, counter, maxCounter);
    }
    return code;
  }

  /**
   * Generates source code for a local correctness method.
   *
   * @private
   * @param  {Object}         program           the parallel program
   * @param  {Array<Object>}  programComponents the parallel program components
   * @param  {Number}         counter           the current counter of the method
   * @param  {Number}         maxCounter        the maximum counter of the methods
   * @return {String}                           a string containing the generated source code
   */
  generateLocalCorrectnessMethod(program, programComponents, counter, maxCounter) {
    const localCorrectnessMethod = {};
    localCorrectnessMethod.name = `LocalCorrectness${counter}`;
    if (counter === 1) {
      localCorrectnessMethod.preconditions = [program.precondition];
      localCorrectnessMethod.postconditions = [];
      for (let programComponent of programComponents) {
        localCorrectnessMethod.postconditions.push(programComponent.precondition);
      }
      localCorrectnessMethod.statements = [];
    } else if (counter < maxCounter) {
      const programComponent = programComponents[counter - 2];
      localCorrectnessMethod.preconditions = [programComponent.precondition];
      localCorrectnessMethod.postconditions = [programComponent.postcondition];
      localCorrectnessMethod.statements = programComponent.body;
    } else {
      localCorrectnessMethod.preconditions = [];
      for (let programComponent of programComponents) {
        localCorrectnessMethod.preconditions.push(programComponent.postcondition);
      }
      localCorrectnessMethod.postconditions = [program.postcondition];
      localCorrectnessMethod.statements = [];
    }
    let code = this.generateMethod(localCorrectnessMethod);
    if (counter !== maxCounter) {
      code += this.newline();
    }
    return code;
  }

  /**
   * Generates source code for the global correctness methods.
   *
   * @private
   * @param  {Object}         program           the parallel program
   * @param  {Array<Object>}  programComponents the parallel program components
   * @return {String}                           a string containing the generated source code
   */
  generateGlobalCorrectnessMethods(program, programComponents) {
    let code = `${this.newline()}${this.tab()}// Global Correctness${this.newline(2)}`;
    let counter = 0;
    for (let programComponent1 of programComponents) {
      for (let programComponent2 of programComponents) {
        if (programComponent1 !== programComponent2) {
          const {
            _code,
            _counter
          } = this.generateGlobalCorrectnessMethod(programComponent1, programComponent2, counter);
          code += _code;
          counter = _counter;
        }
      }
    }
    code = code.slice(0, -this.settings.newline.length);
    return code;
  }

  /**
   * Generates source code for a global correctness method.
   *
   * @private
   * @param  {Object} programComponent1 the first program component
   * @param  {Object} programComponent2 the second program component
   * @param  {Number} counter           the current counter of the method
   * @return {String}                   a string containing the generated source code
   */
  generateGlobalCorrectnessMethod(programComponent1, programComponent2, counter) {
    let code = '';
    for (let assertion1 of programComponent1.assertions) {
      for (let assertion2 of programComponent2.assertions) {
        for (let assertion of [assertion2.precondition, assertion2.postcondition]) {
          code += `${this.tab()}// R: ${programComponent1.name} --- S: ${programComponent2.name}${this.newline()}`;
          const globalCorrectnessMethodPrecondition = {};
          globalCorrectnessMethodPrecondition.name = `GlobalCorrectness${++counter}`;
          globalCorrectnessMethodPrecondition.preconditions = [assertion];
          globalCorrectnessMethodPrecondition.preconditions.push(assertion1.precondition);
          globalCorrectnessMethodPrecondition.postconditions = [assertion];
          globalCorrectnessMethodPrecondition.statements = assertion1.statements;
          code += this.generateMethod(globalCorrectnessMethodPrecondition);
          code += this.newline();
        }
      }
    }
    return {
      _code: code,
      _counter: counter
    };
  }

  /**
   * Generates source code for a method.
   *
   * @private
   * @param  {Object} method the method
   * @return {String}        a string containing the generated source code
   */
  generateMethod(method) {
    let code = this.tab();
    code += `method ${method.name}()${this.newline()}`;
    if (method.statements.length > 0) {
      code += `${this.tab(2)}modifies this;${this.newline()}`;
    }
    for (let precondition of method.preconditions) {
      code += `${this.tab(2)}requires ${precondition}${this.newline()}`;
    }
    for (let postcondition of method.postconditions) {
      code += `${this.tab(2)}ensures ${postcondition}${this.newline()}`;
    }
    code += `${this.tab()}{${this.newline()}`;
    if (method.statements.length > 0) {
      for (let statement of method.statements) {
        code += `${this.tab(2)}${statement}${this.newline()}`;
      }
    }
    code += `${this.tab()}}${this.newline()}`;
    return code;
  }

  /**
   * Returns a specified number of tabs. Default: 1
   *
   * @private
   * @param  {Number} [numTabs = 1] the number of tabs to return
   * @return {String}               a string containing the number of tabs
   */
  tab(numTabs = 1) {
    let tabs = '';
    for (let i = 0; i < numTabs; i++) {
      tabs += this.settings.tab;
    }
    return tabs;
  }

  /**
   * Returns a specified number of newlines. Default: 1
   *
   * @private
   * @param  {Number} [numTabs = 1] the number of newlines to return
   * @return {String}               a string containing the number of newlines
   */
  newline(numNewLines = 1) {
    let newlines = '';
    for (let i = 0; i < numNewLines; i++) {
      newlines += this.settings.newline;
    }
    return newlines;
  }
}
<|endoftext|>