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(); this.body.mark();
// TODO don't mark all methods willy-nilly // TODO don't mark all methods willy-nilly
this.body.markChildren(); this.body.markChildrenIndiscriminately();
} }
addReference () { addReference () {

6
src/ast/nodes/FunctionDeclaration.js

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

5
src/ast/nodes/FunctionExpression.js

@ -1,4 +1,5 @@
import Node from '../Node.js'; import Node from '../Node.js';
import FunctionValue from './shared/FunctionValue.js';
export default class FunctionExpression extends Node { export default class FunctionExpression extends Node {
activate () { activate () {
@ -76,4 +77,8 @@ export default class FunctionExpression extends Node {
markReturnStatements () { markReturnStatements () {
this.returnStatements.forEach( statement => statement.mark() ); 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 relativeId from '../../utils/relativeId.js';
import Node from '../Node.js'; import Node from '../Node.js';
import flatten from '../utils/flatten.js';
import pureFunctions from './shared/pureFunctions.js';
import { unknown } from '../values.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 { export default class MemberExpression extends Node {
bind ( scope ) { bind ( scope ) {
// if this resolves to a namespaced declaration, prepare // if this resolves to a namespaced declaration, prepare
// to replace it // 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' ) { while ( declaration.isNamespace && this.flattened.parts.length ) {
let declaration = scope.findDeclaration( keypath.root.name );
while ( declaration.isNamespace && keypath.parts.length ) {
const exporterId = declaration.module.id; const exporterId = declaration.module.id;
const part = keypath.parts[0]; const part = this.flattened.parts[0];
declaration = declaration.module.traceExport( part.name || part.value ); declaration = declaration.module.traceExport( part.name || part.value );
if ( !declaration ) { if ( !declaration ) {
@ -51,18 +27,20 @@ export default class MemberExpression extends Node {
return; return;
} }
keypath.parts.shift(); this.flattened.parts.shift();
} }
if ( keypath.parts.length ) { if ( this.flattened.parts.length ) {
super.bind( scope ); super.bind( scope );
return; // not a namespaced declaration return; // not a namespaced declaration
} }
// TODO this needs to be the result of calling declaration.run(),
// not the declaration itself. gah
this.declaration = declaration; this.declaration = declaration;
if ( declaration.isExternal ) { 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 ); 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 objectValue = this.object.run();
const propValue = this.computed ? this.property.run() : this.property.name; const propValue = this.computed ? this.property.run() : this.property.name;
@ -93,6 +80,12 @@ export default class MemberExpression extends Node {
values.add( unknown ); // TODO values.add( unknown ); // TODO
} }
initialise ( scope ) {
this.scope = scope;
this.flattened = flatten( this );
super.initialise( scope );
}
mark () { mark () {
if ( this.declaration ) { if ( this.declaration ) {
this.declaration.activate(); this.declaration.activate();
@ -140,6 +133,7 @@ export default class MemberExpression extends Node {
const propValue = this.computed ? this.property.run() : this.property.name; const propValue = this.computed ? this.property.run() : this.property.name;
if ( !objectValue.setProperty ) { if ( !objectValue.setProperty ) {
console.log( `${this}` )
console.log( objectValue ); console.log( objectValue );
throw new Error( `${objectValue} does not have setProperty method` ); 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 ) { render ( code, es ) {
const treeshake = this.module.bundle.treeshake; const treeshake = this.module.bundle.treeshake;
console.group( `rendering ${this}` )
let shouldSeparate = false; let shouldSeparate = false;
let separator; let separator;
@ -44,6 +46,7 @@ export default class VariableDeclaration extends Node {
if ( declarator.id.type === 'Identifier' ) { if ( declarator.id.type === 'Identifier' ) {
const proxy = declarator.proxies.get( declarator.id.name ); const proxy = declarator.proxies.get( declarator.id.name );
const isExportedAndReassigned = !es && proxy.exportName && proxy.isReassigned; const isExportedAndReassigned = !es && proxy.exportName && proxy.isReassigned;
console.log( `declarator.init && declarator.init.isMarked`, declarator.init && declarator.init.isMarked )
if ( isExportedAndReassigned ) { if ( isExportedAndReassigned ) {
if ( declarator.init ) { if ( declarator.init ) {
@ -51,7 +54,7 @@ export default class VariableDeclaration extends Node {
c = declarator.end; c = declarator.end;
empty = false; 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 if ( shouldSeparate ) code.overwrite( c, declarator.start, `${prefix}${this.kind} ` ); // TODO indentation
c = declarator.end; c = declarator.end;
empty = false; empty = false;
@ -90,6 +93,8 @@ export default class VariableDeclaration extends Node {
declarator.render( code, es ); declarator.render( code, es );
} }
console.log( `empty`, empty )
if ( treeshake && empty ) { if ( treeshake && empty ) {
code.remove( this.leadingCommentStart || this.start, this.next || this.end ); code.remove( this.leadingCommentStart || this.start, this.next || this.end );
} else { } else {
@ -103,5 +108,7 @@ export default class VariableDeclaration extends Node {
this.insertSemicolon( code ); 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; if ( this.activated ) return;
this.activated = true; this.activated = true;
console.log( `activating ${this}` )
this.mark(); this.mark();
if ( this.init ) this.init.markChildren(); 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' ) { } else if ( declaration.type === 'ClassDeclaration' ) {
this.values[ name ] = TDZ_VIOLATION; this.values[ name ] = TDZ_VIOLATION;
} else if ( declaration.type === 'FunctionDeclaration' ) { } else if ( declaration.type === 'FunctionDeclaration' ) {
this.values[ name ] = declaration.run(); this.values[ name ] = declaration.reify();
} else { } else {
console.log( declaration ) console.log( declaration )
throw new Error( 'well this is odd' ); 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 ) { export default function flatten ( node ) {
const keypath = node.toString(); // TODO is this the best way?
const parts = []; const parts = [];
while ( node.type === 'MemberExpression' ) { while ( node.type === 'MemberExpression' ) {
if ( node.computed ) return null; if ( node.computed ) {
parts.unshift( node.property.name ); if ( node.type !== 'Literal' || typeof node.value !== 'string' || !validProp.test( node.value ) ) {
return null;
}
}
parts.unshift( node.property );
node = node.object; 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( root );
parts.unshift( name );
return { name, keypath: parts.join( '.' ) }; return { root, name, parts, keypath };
} }

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

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

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

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

Loading…
Cancel
Save