Browse Source

more stuff. still horrible broken

value-tracking
Rich Harris 8 years ago
parent
commit
6b7cdf31a9
  1. 2
      src/ast/nodes/ClassDeclaration.js
  2. 6
      src/ast/nodes/FunctionDeclaration.js
  3. 5
      src/ast/nodes/FunctionExpression.js
  4. 60
      src/ast/nodes/MemberExpression.js
  5. 9
      src/ast/nodes/VariableDeclaration.js
  6. 2
      src/ast/nodes/VariableDeclarator.js
  7. 2
      src/ast/scopes/Scope.js
  8. 30
      src/ast/utils/flatten.js
  9. 1
      test/form/exclude-unnecessary-modifications/_config.js
  10. 1
      test/form/namespace-optimization/_config.js

2
src/ast/nodes/ClassDeclaration.js

@ -20,7 +20,7 @@ export default class ClassDeclaration extends Node {
this.body.mark();
// TODO don't mark all methods willy-nilly
this.body.markChildren();
this.body.markChildrenIndiscriminately();
}
addReference () {

6
src/ast/nodes/FunctionDeclaration.js

@ -49,6 +49,10 @@ export default class FunctionDeclaration extends Node {
this.body.initialise();
}
reify () {
return new FunctionValue( this );
}
render ( code, es ) {
if ( !this.module.bundle.treeshake || this.activated ) {
super.render( code, es );
@ -58,6 +62,6 @@ export default class FunctionDeclaration extends Node {
}
run () {
return new FunctionValue( this );
// noop
}
}

5
src/ast/nodes/FunctionExpression.js

@ -1,4 +1,5 @@
import Node from '../Node.js';
import FunctionValue from './shared/FunctionValue.js';
export default class FunctionExpression extends Node {
activate () {
@ -76,4 +77,8 @@ export default class FunctionExpression extends Node {
markReturnStatements () {
this.returnStatements.forEach( statement => statement.mark() );
}
run () {
return new FunctionValue( this );
}
}

60
src/ast/nodes/MemberExpression.js

@ -1,44 +1,20 @@
import relativeId from '../../utils/relativeId.js';
import Node from '../Node.js';
import flatten from '../utils/flatten.js';
import pureFunctions from './shared/pureFunctions.js';
import { unknown } from '../values.js';
const validProp = /^[a-zA-Z_$][a-zA-Z_$0-9]*$/;
class Keypath {
constructor ( node ) {
this.parts = [];
while ( node.type === 'MemberExpression' ) {
const prop = node.property;
if ( node.computed ) {
if ( prop.type !== 'Literal' || typeof prop.value !== 'string' || !validProp.test( prop.value ) ) {
this.computed = true;
return;
}
}
this.parts.unshift( prop );
node = node.object;
}
this.root = node;
}
}
export default class MemberExpression extends Node {
bind ( scope ) {
// if this resolves to a namespaced declaration, prepare
// to replace it
const keypath = new Keypath( this );
if ( this.flattened && this.flattened.root.type === 'Identifier' ) {
let declaration = scope.findDeclaration( this.flattened.root.name );
if ( !keypath.computed && keypath.root.type === 'Identifier' ) {
let declaration = scope.findDeclaration( keypath.root.name );
while ( declaration.isNamespace && keypath.parts.length ) {
while ( declaration.isNamespace && this.flattened.parts.length ) {
const exporterId = declaration.module.id;
const part = keypath.parts[0];
const part = this.flattened.parts[0];
declaration = declaration.module.traceExport( part.name || part.value );
if ( !declaration ) {
@ -51,18 +27,20 @@ export default class MemberExpression extends Node {
return;
}
keypath.parts.shift();
this.flattened.parts.shift();
}
if ( keypath.parts.length ) {
if ( this.flattened.parts.length ) {
super.bind( scope );
return; // not a namespaced declaration
}
// TODO this needs to be the result of calling declaration.run(),
// not the declaration itself. gah
this.declaration = declaration;
if ( declaration.isExternal ) {
declaration.module.suggestName( keypath.root.name );
declaration.module.suggestName( this.flattened.root.name );
}
}
@ -76,6 +54,15 @@ export default class MemberExpression extends Node {
return this.declaration.call( undefined, args );
}
// TODO a better representation of these functions
if ( this.flattened && this.flattened.keypath in pureFunctions ) {
const declaration = this.scope.findDeclaration( this.flattened.name );
if ( declaration.isGlobal ) {
args.forEach( arg => arg.run() );
return;
}
}
const objectValue = this.object.run();
const propValue = this.computed ? this.property.run() : this.property.name;
@ -93,6 +80,12 @@ export default class MemberExpression extends Node {
values.add( unknown ); // TODO
}
initialise ( scope ) {
this.scope = scope;
this.flattened = flatten( this );
super.initialise( scope );
}
mark () {
if ( this.declaration ) {
this.declaration.activate();
@ -140,6 +133,7 @@ export default class MemberExpression extends Node {
const propValue = this.computed ? this.property.run() : this.property.name;
if ( !objectValue.setProperty ) {
console.log( `${this}` )
console.log( objectValue );
throw new Error( `${objectValue} does not have setProperty method` );
}

9
src/ast/nodes/VariableDeclaration.js

@ -25,6 +25,8 @@ export default class VariableDeclaration extends Node {
render ( code, es ) {
const treeshake = this.module.bundle.treeshake;
console.group( `rendering ${this}` )
let shouldSeparate = false;
let separator;
@ -44,6 +46,7 @@ export default class VariableDeclaration extends Node {
if ( declarator.id.type === 'Identifier' ) {
const proxy = declarator.proxies.get( declarator.id.name );
const isExportedAndReassigned = !es && proxy.exportName && proxy.isReassigned;
console.log( `declarator.init && declarator.init.isMarked`, declarator.init && declarator.init.isMarked )
if ( isExportedAndReassigned ) {
if ( declarator.init ) {
@ -51,7 +54,7 @@ export default class VariableDeclaration extends Node {
c = declarator.end;
empty = false;
}
} else if ( !treeshake || proxy.activated ) {
} else if ( !treeshake || proxy.activated || ( declarator.init && declarator.init.isMarked ) ) {
if ( shouldSeparate ) code.overwrite( c, declarator.start, `${prefix}${this.kind} ` ); // TODO indentation
c = declarator.end;
empty = false;
@ -90,6 +93,8 @@ export default class VariableDeclaration extends Node {
declarator.render( code, es );
}
console.log( `empty`, empty )
if ( treeshake && empty ) {
code.remove( this.leadingCommentStart || this.start, this.next || this.end );
} else {
@ -103,5 +108,7 @@ export default class VariableDeclaration extends Node {
this.insertSemicolon( code );
}
}
console.groupEnd()
}
}

2
src/ast/nodes/VariableDeclarator.js

@ -52,6 +52,8 @@ export default class VariableDeclarator extends Node {
if ( this.activated ) return;
this.activated = true;
console.log( `activating ${this}` )
this.mark();
if ( this.init ) this.init.markChildren();
}

2
src/ast/scopes/Scope.js

@ -127,7 +127,7 @@ export default class Scope {
} else if ( declaration.type === 'ClassDeclaration' ) {
this.values[ name ] = TDZ_VIOLATION;
} else if ( declaration.type === 'FunctionDeclaration' ) {
this.values[ name ] = declaration.run();
this.values[ name ] = declaration.reify();
} else {
console.log( declaration )
throw new Error( 'well this is odd' );

30
src/ast/utils/flatten.js

@ -1,16 +1,34 @@
const validProp = /^[a-zA-Z_$][a-zA-Z_$0-9]*$/;
export default function flatten ( node ) {
const keypath = node.toString(); // TODO is this the best way?
const parts = [];
while ( node.type === 'MemberExpression' ) {
if ( node.computed ) return null;
parts.unshift( node.property.name );
if ( node.computed ) {
if ( node.type !== 'Literal' || typeof node.value !== 'string' || !validProp.test( node.value ) ) {
return null;
}
}
parts.unshift( node.property );
node = node.object;
}
if ( node.type !== 'Identifier' ) return null;
const root = node;
let name;
if ( root.type === 'Identifier' ) {
name = root.name;
} else if ( root.type === 'ThisExpression' ) {
name = 'this';
} else if ( root.type === 'Super' ) {
name = 'super';
} else {
return null;
}
const name = node.name;
parts.unshift( name );
parts.unshift( root );
return { name, keypath: parts.join( '.' ) };
return { root, name, parts, keypath };
}

1
test/form/exclude-unnecessary-modifications/_config.js

@ -1,3 +1,4 @@
module.exports = {
solo: true,
description: 'statements that modify definitions within unused functions are excluded'
};

1
test/form/namespace-optimization/_config.js

@ -1,4 +1,3 @@
module.exports = {
solo: true,
description: 'it does static lookup optimization of internal namespaces'
};

Loading…
Cancel
Save