Browse Source

Statically resolve internal namespace access

gh-109
Oskar Segersvärd 9 years ago
parent
commit
59fff295e4
  1. 76
      src/Statement.js
  2. 3
      test/function/namespace-optimization/_config.js
  3. 3
      test/function/namespace-optimization/bar.js
  4. 3
      test/function/namespace-optimization/foo.js
  5. 3
      test/function/namespace-optimization/main.js
  6. 1
      test/function/namespace-optimization/quux.js

76
src/Statement.js

@ -31,6 +31,10 @@ export default class Statement {
this.reassigns = blank(); this.reassigns = blank();
// TODO: make this more efficient
this.dependantIds = [];
this.namespaceReplacements = [];
this.isIncluded = false; this.isIncluded = false;
this.isImportDeclaration = node.type === 'ImportDeclaration'; this.isImportDeclaration = node.type === 'ImportDeclaration';
@ -126,6 +130,10 @@ export default class Statement {
// /update expressions) need to be captured // /update expressions) need to be captured
let writeDepth = 0; let writeDepth = 0;
// Used to track
let currentMemberExpression = null;
let namespace = null;
if ( !this.isImportDeclaration ) { if ( !this.isImportDeclaration ) {
walk( this.node, { walk( this.node, {
enter: ( node, parent ) => { enter: ( node, parent ) => {
@ -142,6 +150,47 @@ export default class Statement {
if ( /Function/.test( node.type ) && !isIife( node, parent ) ) readDepth -= 1; if ( /Function/.test( node.type ) && !isIife( node, parent ) ) readDepth -= 1;
if ( node._scope ) scope = scope.parent; if ( node._scope ) scope = scope.parent;
if ( node.type === 'MemberExpression' && ( !currentMemberExpression || node.object === currentMemberExpression ) ) {
currentMemberExpression = node;
if ( !namespace ) {
const id = this.module.locals.lookup( node.object.name );
if ( !id || !id.isModule || id.isExternal ) return;
namespace = id;
}
const name = node.property.name || ( node.property.type === 'Literal' ? node.property.value : null );
if ( !name ) {
namespace.dynamicAccess();
namespace = null;
currentMemberExpression = null;
return;
}
const id = namespace.exports.lookup( name );
if ( !id ) {
throw new Error( `Module doesn't define "${name}"!` );
}
// We can't resolve deeper. Replace the member chain.
if ( parent.type !== 'MemberExpression' || !( id.isModule && !id.isExternal ) ) {
if ( !~this.dependantIds.indexOf( id ) ) {
this.dependantIds.push( id );
}
this.namespaceReplacements.push( [ node, id ] );
namespace = null;
currentMemberExpression = null;
return;
}
namespace = id;
}
} }
}); });
} }
@ -272,6 +321,19 @@ export default class Statement {
return; return;
} }
this.dependantIds.forEach( id => {
// FIXME: what should be done about modules?
if ( id.isModule ) {
if ( id.isExternal ) {
} else {
}
}
id.module && id.module.markExport( id.originalName, this.module );
});
keys( this.dependsOn ).forEach( name => { keys( this.dependsOn ).forEach( name => {
if ( this.defines[ name ] ) return; // TODO maybe exclude from `this.dependsOn` in the first place? if ( this.defines[ name ] ) return; // TODO maybe exclude from `this.dependsOn` in the first place?
this.module.mark( name ); this.module.mark( name );
@ -279,6 +341,8 @@ export default class Statement {
} }
replaceIdentifiers ( magicString, names, bundleExports ) { replaceIdentifiers ( magicString, names, bundleExports ) {
const statement = this;
const replacementStack = []; const replacementStack = [];
const nameList = keys( names ); const nameList = keys( names );
@ -366,6 +430,18 @@ export default class Statement {
replacementStack.push( newNames ); replacementStack.push( newNames );
} }
if ( node.type === 'MemberExpression' ) {
const replacements = statement.namespaceReplacements;
for ( let i = 0; i < replacements.length; i += 1 ) {
const [ top, id ] = replacements[ i ];
if ( node === top ) {
magicString.overwrite( node.start, node.end, id.name );
return this.skip();
}
}
}
if ( node.type !== 'Identifier' ) return; if ( node.type !== 'Identifier' ) return;
// if there's no replacement, or it's the same, there's nothing more to do // if there's no replacement, or it's the same, there's nothing more to do

3
test/function/namespace-optimization/_config.js

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

3
test/function/namespace-optimization/bar.js

@ -0,0 +1,3 @@
import * as quux from './quux';
export { quux };

3
test/function/namespace-optimization/foo.js

@ -0,0 +1,3 @@
import * as bar from './bar';
export { bar };

3
test/function/namespace-optimization/main.js

@ -0,0 +1,3 @@
import * as foo from './foo';
foo.bar.quux.a();

1
test/function/namespace-optimization/quux.js

@ -0,0 +1 @@
export function a () {}
Loading…
Cancel
Save