Skip to content

Commit

Permalink
add other assignment operator support
Browse files Browse the repository at this point in the history
  • Loading branch information
pissang committed Jan 22, 2013
1 parent 066ef86 commit 6aa629d
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 24 deletions.
107 changes: 83 additions & 24 deletions dfatool.js
Expand Up @@ -816,28 +816,20 @@ Scope.prototype.initialize = function(){
return false;
case "VariableDeclarator":
if( node["init"]){
var dfNode = scope.assign(node["id"], node["init"], scope.offsetLoc(node.loc.end) );
// in the case "var a = b = c;"
if( node["init"].type == "AssignmentExpression"){
processChainedAssignment( node["init"], currentBranch, scope.offsetLoc(node.loc.start) );
var dfNode = scope.assign(node["id"], node["init"]["left"], scope.offsetLoc(node.loc.end) );
}else{
var dfNode = scope.assign(node["id"], node["init"], scope.offsetLoc(node.loc.end) );
}
if( currentBranch && dfNode ){
dfNode.conditionalStatement = currentBranch;
}
return false;
}
break;
return false;
case "AssignmentExpression":
// support the chained assign statement like "module.q = module.Q = module.dom.q";
// will be convert to module.Q = module.dom.q; module.q = module.dom.q
var leftNodes = [node.left],
rightMost = node.right;
while( rightMost.type == "AssignmentExpression" ){
leftNodes.unshift(rightMost.left);
rightMost = rightMost.right;
}
leftNodes.forEach(function(leftNode){
var dfNode = scope.assign( leftNode, rightMost, scope.offsetLoc(node.loc.end) );
if( currentBranch && dfNode ){
dfNode.conditionalStatement = currentBranch;
}
})
processChainedAssignment( node, currentBranch, scope.offsetLoc(node.loc.start) );
return false;
case "ExpressionStatement":
// if the statement is a single call expression
Expand Down Expand Up @@ -900,6 +892,39 @@ Scope.prototype.initialize = function(){
} )
}

function processChainedAssignment( node, currentBranch, baseLoc ){
// support the chained assign statement like "module.q = module.Q = module.dom.q";
// will be convert to module.Q = module.dom.q; module.q = module.Q
var nodes = [],
operators = [],
current = node;
while( current.type == "AssignmentExpression" ){
nodes.unshift(current.left);
operators.unshift(current.operator);
current = current.right;
}
nodes.unshift(current);
// location of each assign item need to be reverted;
// like a = b = c, statment b=c need to be put in the left most, and then a = b;
// TODO: here simple revert still has some problem, for example fooooooo = a = b;
// the location of a is behind the statement of a = b, so value of fooooooo is still
// equal to previous value of a;
var loc = baseLoc;
for(var i = 0; i < nodes.length-1; i++){
var rightNode = nodes[i],
leftNode = nodes[i+1],
operator = operators[i];
loc += scope.offsetLoc(rightNode.loc.end) - scope.offsetLoc(leftNode.loc.start);
var dfNode = scope.assign( leftNode, rightNode, loc);
if( dfNode ){
dfNode.operator = operator;
if( currentBranch){
dfNode.conditionalStatement = currentBranch;
}
}
}
}

initializeInBranch( this.ast, null );
}
// spread the expression of each value to a fix point
Expand Down Expand Up @@ -1209,7 +1234,28 @@ Variable.merge = function(nodes){
if( ! node.accessPath ){// reaching definition
var value = node.value;
// create a new value with derived ast
ret = new Snapshot( value );
if( node.operator == "="){
ret = new Snapshot( value );
// consider the compute assign operator
// += -= /= *= %=....
}else if(node.operator.substr(1) == "="){
var snapshot = new Snapshot( value);
if( snapshot.type == "literal"
&& ret.type == "literal"){
try{
eval( "ret.ast.value "+node.operator+" snapshot.ast.value");
}catch(e){
log("Unkonwn operator "+node.operator);
}
ret.update(ret.ast);
}else{ // convert to binary expression
var bexp = TEMPLATE_BINARY_EXPRESSION(node.operator.substr(0, 1), ret.ast, snapshot.ast);
bexp.loc = snapshot.ast.loc;
snapshot.update( bexp );
ret = snapshot;
}
}

}else{ //assign an property
if(ret){
var value = node.value;
Expand Down Expand Up @@ -1261,7 +1307,8 @@ var AssignStatement = function(accessPath, rhs, scope){
this.attachValue( new Value(rhs, this.scope) );
}
this.loc = 0;

// operator canbe =, +=, -=, *=, /=, %=
this.operator = "=";
// the value is assigend in a conditional statement
// {ConditionalStatement}
this.conditionalStatement = null;
Expand Down Expand Up @@ -1290,6 +1337,7 @@ AssignStatement.prototype.attachValue = function(value, keepName /*optional*/){
// @read-only
keepName = isUndefined( keepName ) ? false : keepName;
if( ! keepName ){
// PENDING: distinguish the property name with variable name??
if( this.accessPath ){
this.accessPath = Value.prototype.solveAccessPath( this.accessPath );
var last = this.accessPath[ this.accessPath.length-1 ];
Expand Down Expand Up @@ -1495,7 +1543,6 @@ Value.prototype.update = function(ast){
default:
break;
}
// set buildin

}
// add a prefix to avoid confict with native property like toString
Expand Down Expand Up @@ -1780,7 +1827,6 @@ var Snapshot = function( value ){

Value.call( this, value.getDerivedAst(), value.scope );
this.name = value.name;

}

Snapshot.prototype = Value.prototype;
Expand Down Expand Up @@ -2411,8 +2457,8 @@ ConditionalTree.prototype.findNode = function( conditionalStatement ){
return child;
}, this.root);
}
ConditionalTree.prototype.buildAST = function(){
return this.root.buildAST( this.variable.name );
ConditionalTree.prototype.buildAST = function( name ){
return this.root.buildAST( name || this.variable.name );
}
ConditionalTree.Node = function( statement ){

Expand Down Expand Up @@ -2476,7 +2522,7 @@ ConditionalTree.Node.prototype.build = function( statement ){
ConditionalTree.Node.prototype.buildAST = function( varName ){
var consequent = TEMPLATE_BLOCK();
this.consequent.forEach( function( node ){
consequent.body.push( node(buildAST) );
consequent.body.push( buildAST(node) );
} );
if( this.alternate.length ){
var alternate = TEMPLATE_BLOCK();
Expand Down Expand Up @@ -2704,6 +2750,19 @@ function asLibrary(variable){
//=========================================
// exports method
//=========================================
exports.TEMPLATE_BLOCK = TEMPLATE_BLOCK
exports.TEMPLATE_IDENTIFIER = TEMPLATE_IDENTIFIER
exports.TEMPLATE_FUNCTION_DECLARATION = TEMPLATE_FUNCTION_DECLARATION
exports.TEMPLATE_ASSIGNMENT_EXPRESSION = TEMPLATE_ASSIGNMENT_EXPRESSION
exports.TEMPLATE_EXPRESSION_STATEMENT = TEMPLATE_EXPRESSION_STATEMENT
exports.TEMPLATE_VARIABLE_DECLARATOR = TEMPLATE_VARIABLE_DECLARATOR
exports.TEMPLATE_OBJECT_EXPRESSION = TEMPLATE_OBJECT_EXPRESSION
exports.TEMPLATE_LITERAL_EXPRESSION = TEMPLATE_LITERAL_EXPRESSION
exports.TEMPLATE_ARRAY_EXPRESSION = TEMPLATE_ARRAY_EXPRESSION
exports.TEMPLATE_BINARY_EXPRESSION = TEMPLATE_BINARY_EXPRESSION
exports.TEMPLATE_UNARY_EXPRESSION = TEMPLATE_UNARY_EXPRESSION
exports.TEMPLATE_IF_STATEMENT = TEMPLATE_IF_STATEMENT
exports.TEMPLATE_MEMBER_EXPRESSION = TEMPLATE_MEMBER_EXPRESSION

exports.walk = walk;
exports.buildScope = buildScope;
Expand Down
12 changes: 12 additions & 0 deletions tests/cases/assign_operator.js
@@ -0,0 +1,12 @@
var origin = 10;
var sum = origin;
var minus = origin;
var product = origin;
var quotient = origin;
var remainder = origin;

sum += 10;
minus -= 10;
product *= 10;
quotient /= 10;
remainder %= 10;
5 changes: 5 additions & 0 deletions tests/cases/chained_assign.js
@@ -0,0 +1,5 @@
var foo = 10;
var bar = foo = 20;

var something;
something = foo = 30;
37 changes: 37 additions & 0 deletions tests/outline.js
@@ -0,0 +1,37 @@
var esprima = require('esprima');
var dfatool = require('../dfatool');
var fs = require('fs');
var util = require('util');
var codegen = require('escodegen');

var argv = process.argv.slice(2);
var fileName = argv[0];

if( ! fileName){
console.error("Input scripts needed, 'node outline.js cases/array.js'");
return;
}
fs.readFile(fileName, 'utf-8', function(err, data){

var ast = esprima.parse(data, {
loc : true
});
var globalScope = dfatool.globalScope
dfatool.buildScope(ast, globalScope);

globalScope.initialize();
globalScope.derivation()

var outline = {};
for(var name in globalScope._defines){
var variable = globalScope._defines[name];
var value = variable.inference();
if( value ){
outline[variable.name] = value.toJSON();
}
}

console.log(util.inspect(outline, false, null));

dfatool.flushlog("log.txt");
})

0 comments on commit 6aa629d

Please sign in to comment.