@awayfl/avm2
Version:
Virtual machine for executing AS3 code
132 lines (102 loc) • 2.89 kB
text/typescript
import { IGenerator } from './IGenerator';
import { ILexGenerator } from './LexImportsGenerator';
import { Multiname } from '../abc/lazy/Multiname';
import { Scope } from '../run/Scope';
export interface ICallEntry {
name?: Multiname;
alias: string;
isMangled: boolean;
fromLex: boolean;
index: number;
isFunc?: boolean;
methodAlias?: string;
methodImport?: string;
}
/**
* @description Generate a simple call instruction (witout runtime checks) on lexed alliases
*/
export class FastCall implements IGenerator {
public safeIntrDistance = 20;
private _active: StringMap<ICallEntry> = {};
private _imports: ICallEntry[] = [];
constructor(public lexer: ILexGenerator, public scope: Scope = null) {}
checkNameToCall(mn: Multiname, propname: string, lex = false) {
if (!this.scope) {
return false;
}
const obj = this.scope.findScopeProperty(mn, true, false);
if (!lex) {
return obj && typeof obj[propname] === 'function';
}
return obj && obj[mn.getMangledName()] && typeof obj[mn.getMangledName()][propname] === 'function';
}
mark(alias: string, index: number, isMangled: boolean, name: Multiname = null, fromLex = false) {
this._active[alias] = { alias, index, name, isMangled, fromLex };
}
sureThatFast(stackAlias: string, method?: string): ICallEntry {
const d = this._active[stackAlias];
if (!d || (method && !d.name)) {
return null;
}
if (method && this.checkNameToCall(d.name, method, d.fromLex)) {
d.isFunc = true;
}
return d;
}
getMethodAlias(stackAlias: string, method?: string): string | null {
const d = this._active[stackAlias];
if (!d || !d.isFunc || !d.name) {
return null;
}
const lexedImportD = this.lexer.findAliases(d.name, false)[0];
if (!lexedImportD) {
return null;
}
const lexedImport = lexedImportD.alias;
const methodALias = lexedImport + '_' + method;
const declare = this._imports.find((e) => e.methodAlias === methodALias);
if (declare) {
return declare.methodAlias;
} else {
d.methodAlias = methodALias;
d.methodImport = lexedImport + '.' + method;
this._imports.push(d);
}
return methodALias;
}
kill(stackAlias: string) {
delete this._active[stackAlias];
}
killFar(index: number) {
const keys = Object.keys(this._active);
for (const k of keys) {
if (index - this._active[k].index >= this.safeIntrDistance) {
delete this._active[k];
}
}
}
genHeader(ident: string): string {
const header = [`${ident} /* ${this.constructor.name} */`];
for (const d of this._imports) {
header.push(`${ident} const ${d.methodAlias} = ${d.methodImport};`);
}
header.push('\n');
return header.join('\n');
}
genBody(): string {
return '';
}
/**
* Drop fastCall cache
*/
drop() {
this._active = {};
}
genPost(arr: string[]) {
return arr;
}
reset() {
this._active = {};
this._imports = [];
}
}