cst
Version:
JavaScript CST Implementation
445 lines (386 loc) • 17 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _toConsumableArray2 = require('babel-runtime/helpers/toConsumableArray');
var _toConsumableArray3 = _interopRequireDefault(_toConsumableArray2);
var _extends2 = require('babel-runtime/helpers/extends');
var _extends3 = _interopRequireDefault(_extends2);
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
var _getIterator3 = _interopRequireDefault(_getIterator2);
var _map = require('babel-runtime/core-js/map');
var _map2 = _interopRequireDefault(_map);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _Reference = require('./Reference');
var _Reference2 = _interopRequireDefault(_Reference);
var _Variable = require('./Variable');
var _Variable2 = _interopRequireDefault(_Variable);
var _Definition = require('./Definition');
var _Definition2 = _interopRequireDefault(_Definition);
var _toArray = require('../../utils/toArray');
var _toArray2 = _interopRequireDefault(_toArray);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var Scope = function () {
function Scope(scopeInfo) {
(0, _classCallCheck3.default)(this, Scope);
var node = scopeInfo.node,
parentScope = scopeInfo.parentScope,
isProgramScope = scopeInfo.isProgramScope,
isFunctionScope = scopeInfo.isFunctionScope,
isClassScope = scopeInfo.isClassScope,
isArrowFunctionScope = scopeInfo.isArrowFunctionScope;
this.node = node;
this.parentScope = parentScope;
if (parentScope) {
parentScope.childScopes.push(this);
this._depth = parentScope._depth + 1;
} else {
this._depth = 0;
}
this.childScopes = [];
this._variables = new _map2.default();
this._references = new _map2.default();
this._isProgramScope = Boolean(isProgramScope);
this._isFunctionScope = Boolean(isFunctionScope);
this._isClassScope = Boolean(isClassScope);
this._isArrowFunctionScope = Boolean(isArrowFunctionScope);
if (isProgramScope) {
this._programReferences = new _map2.default();
this._programDefinitions = new _map2.default();
}
}
(0, _createClass3.default)(Scope, [{
key: '_addVariable',
value: function _addVariable(variable) {
var variables = this._variables.get(variable.name);
if (variables) {
variables.push(variable);
variables.sort(function (variable1, variable2) {
var typeOrder1 = _Definition.typeOrder[variable1.type];
var typeOrder2 = _Definition.typeOrder[variable2.type];
if (typeOrder1 > typeOrder2) {
return 1;
}
if (typeOrder1 < typeOrder2) {
return -1;
}
return 0;
});
} else {
this._variables.set(variable.name, [variable]);
}
}
}, {
key: '_addDefinition',
value: function _addDefinition(definitionInfo) {
var node = definitionInfo.node,
name = definitionInfo.name,
type = definitionInfo.type;
if (type === _Definition.types.Variable) {
if (!this._isFunctionScope && this.parentScope) {
this.parentScope._addDefinition(definitionInfo);
return;
}
}
var variables = this._variables.get(name) || [];
var variable = void 0;
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)(variables), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var item = _step.value;
if (item.type === type) {
variable = item;
break;
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
if (!variable) {
variable = new _Variable2.default({ name: name, type: type, scope: this });
this._adjustReferencesOnVariableAdd(variable);
this._addVariable(variable);
}
var definition = new _Definition2.default({ node: node, type: type, scope: this });
variable._addDefinition(definition);
var programScope = this._getProgramScope();
if (programScope) {
programScope._programDefinitions.set(node, definition);
}
}
}, {
key: '_removeDefinition',
value: function _removeDefinition(definition) {
var variable = definition.variable;
variable._removeDefinition(definition);
if (variable._definitions.size === 0 && (variable.type === 'LetVariable' || variable.type === 'Constant' || variable.type === 'Variable' || variable.type === 'Parameter' || variable.type === 'SelfReference' || variable.type === 'CatchClauseError' || variable.type === 'ImportBinding')) {
removeVariable(variable);
}
var programScope = this._getProgramScope();
if (programScope) {
programScope._programDefinitions.delete(definition.node);
}
}
}, {
key: '_adjustReferencesOnVariableAdd',
value: function _adjustReferencesOnVariableAdd(variable) {
var depth = variable.scope._depth;
var references = this._references.get(variable.name);
if (references) {
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = (0, _getIterator3.default)(references), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var reference = _step2.value;
var refVar = reference.variable;
var varDepth = refVar.scope._depth;
if (varDepth === depth) {
if (_Definition.typeOrder[variable.type] < _Definition.typeOrder[refVar.type]) {
refVar._transferReferences(variable);
removeVariableIfRequired(refVar);
}
} else if (varDepth < depth) {
refVar._references.delete(reference);
variable._addReference(reference);
reference.variable = variable;
removeVariableIfRequired(refVar);
}
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
}
var _iteratorNormalCompletion3 = true;
var _didIteratorError3 = false;
var _iteratorError3 = undefined;
try {
for (var _iterator3 = (0, _getIterator3.default)(this.childScopes), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
var childScope = _step3.value;
childScope._adjustReferencesOnVariableAdd(variable);
}
} catch (err) {
_didIteratorError3 = true;
_iteratorError3 = err;
} finally {
try {
if (!_iteratorNormalCompletion3 && _iterator3.return) {
_iterator3.return();
}
} finally {
if (_didIteratorError3) {
throw _iteratorError3;
}
}
}
}
}, {
key: '_addReference',
value: function _addReference(referenceInfo) {
var name = referenceInfo.name;
var reference = new _Reference2.default((0, _extends3.default)({ scope: this }, referenceInfo));
this._assignReference(reference, name);
var references = this._references.get(name);
if (references) {
references.push(reference);
} else {
this._references.set(name, [reference]);
}
var programScope = this._getProgramScope();
if (programScope) {
programScope._programReferences.set(reference.node, reference);
}
}
}, {
key: '_assignReference',
value: function _assignReference(reference, name) {
var currentScope = this;
do {
var variables = currentScope._variables.get(name);
if (variables) {
if (reference.type) {
var _iteratorNormalCompletion4 = true;
var _didIteratorError4 = false;
var _iteratorError4 = undefined;
try {
for (var _iterator4 = (0, _getIterator3.default)(variables), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
var variable = _step4.value;
if (variable.type === reference.type) {
variable._addReference(reference);
return;
}
}
} catch (err) {
_didIteratorError4 = true;
_iteratorError4 = err;
} finally {
try {
if (!_iteratorNormalCompletion4 && _iterator4.return) {
_iterator4.return();
}
} finally {
if (_didIteratorError4) {
throw _iteratorError4;
}
}
}
} else {
variables[0]._addReference(reference);
return;
}
}
if (!currentScope.parentScope) {
var globalVariable = new _Variable2.default({
name: name, type: _Definition.types.ImplicitGlobal, scope: currentScope
});
globalVariable._addReference(reference);
currentScope._addVariable(globalVariable);
return;
} else {
if ((name === 'arguments' || name === 'this') && currentScope._isFunctionScope && !currentScope._isArrowFunctionScope && !currentScope._isProgramScope || name === 'super' && currentScope._isClassScope) {
var builtInVariable = new _Variable2.default({
name: name, type: _Definition.types.BuiltIn, scope: currentScope
});
builtInVariable._addReference(reference);
currentScope._addVariable(builtInVariable);
return;
}
currentScope = currentScope.parentScope;
}
} while (true);
}
}, {
key: '_removeReference',
value: function _removeReference(reference) {
var variable = reference.variable;
var name = variable.name;
var references = this._references.get(name);
if (references) {
var index = references.indexOf(reference);
if (index !== -1) {
references.splice(index, 1);
}
}
variable._removeReference(reference);
if (variable._references.size === 0 && (variable.type === 'ImplicitGlobal' || variable.type === 'BuiltIn')) {
removeVariable(variable);
}
var programScope = this._getProgramScope();
if (programScope) {
programScope._programReferences.delete(reference.node);
}
}
}, {
key: '_getProgramScope',
value: function _getProgramScope() {
var scope = this;
while (scope && !scope._isProgramScope) {
scope = scope.parentScope;
}
return scope;
}
}, {
key: 'getVariables',
value: function getVariables() {
var _ref;
return (_ref = []).concat.apply(_ref, (0, _toConsumableArray3.default)((0, _toArray2.default)(this._variables.values())));
}
}, {
key: 'getReferences',
value: function getReferences() {
var _ref2;
return (_ref2 = []).concat.apply(_ref2, (0, _toConsumableArray3.default)((0, _toArray2.default)(this._references.values())));
}
}, {
key: 'destroy',
value: function destroy() {
var parentScope = this.parentScope;
if (parentScope) {
var scopeIndex = parentScope.childScopes.indexOf(this);
if (scopeIndex !== -1) {
parentScope.childScopes.splice(scopeIndex, 1);
}
}
this.getReferences().forEach(this._removeReference, this);
}
}]);
return Scope;
}();
exports.default = Scope;
function removeVariableIfRequired(variable) {
if (variable._references.size === 0 && variable._definitions.size === 0) {
var variables = variable.scope._variables.get(variable.name);
if (variables) {
var index = variables.indexOf(variable);
if (index !== -1) {
variables.splice(index, 1);
}
if (variables.length === 0) {
variable.scope._variables.delete(variable.name);
}
}
}
}
function removeVariable(variable) {
var scope = variable.scope;
var variables = scope._variables.get(variable.name);
if (variables) {
var index = variables.indexOf(variable);
if (index !== -1) {
variables.splice(index, 1);
if (variables.length === 0) {
scope._variables.delete(variable.name);
}
var _iteratorNormalCompletion5 = true;
var _didIteratorError5 = false;
var _iteratorError5 = undefined;
try {
for (var _iterator5 = (0, _getIterator3.default)(variable._references), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
var reference = _step5.value;
reference.scope._assignReference(reference, variable.name);
}
} catch (err) {
_didIteratorError5 = true;
_iteratorError5 = err;
} finally {
try {
if (!_iteratorNormalCompletion5 && _iterator5.return) {
_iterator5.return();
}
} finally {
if (_didIteratorError5) {
throw _iteratorError5;
}
}
}
}
}
}
//# sourceMappingURL=Scope.js.map