@awayfl/avm2
Version:
Virtual machine for executing AS3 code
236 lines (235 loc) • 9 kB
JavaScript
import { Settings } from '../Settings';
import { emitInlineLocal, emitInlineStack } from './emiters';
var CompilerState = /** @class */ (function () {
function CompilerState(methodInfo) {
this._indent = '';
this._indentLen = 0;
this.names = [];
this.openTryCatchGroups = [];
// same as js0 in compile
this.mainBlock = [];
// same as js in compile
this.headerBlock = [];
this.thisAliases = new Set();
this.constAliases = {};
// back ref to local-> stack
this.localAliases = {};
this.localTypes = {};
this.stackAliases = {};
this.noHoistMultiname = Settings.NO_HOIST_MULTINAME;
this.methodInfo = methodInfo;
this.abc = methodInfo.abc;
this.init();
}
Object.defineProperty(CompilerState.prototype, "indent", {
get: function () {
return this._indent;
},
enumerable: false,
configurable: true
});
CompilerState.prototype.setStackAlias = function (stackIndex, alias) {
if (alias === void 0) { alias = null; }
var realIndex = this.evalStackIndex(stackIndex);
this.stackAliases[realIndex] = alias;
return alias;
};
CompilerState.prototype.getStackAlias = function (stackIndex) {
return this.stackAliases[this.evalStackIndex(stackIndex)];
};
Object.defineProperty(CompilerState.prototype, "isPossibleGlobalThis", {
get: function () {
var kind = this.methodInfo.trait && this.methodInfo.trait.kind;
// because in AS3 methods/get/set is stricted with this, it can't be global
return (kind !== 1 /* TRAIT.Method */ &&
kind !== 2 /* TRAIT.Getter */ &&
kind !== 3 /* TRAIT.Setter */ &&
!this.methodInfo.isConstructor);
},
enumerable: false,
configurable: true
});
Object.defineProperty(CompilerState.prototype, "canUseRealThis", {
get: function () {
if (!Settings.EMIT_REAL_THIS)
return false;
return !this.isPossibleGlobalThis;
},
enumerable: false,
configurable: true
});
CompilerState.prototype.init = function () {
if (this.methodInfo.parentInfo) {
this.localTypes[0] = [this.methodInfo.parentInfo.typeName];
}
var i = 1;
for (var _i = 0, _a = this.methodInfo.parameters; _i < _a.length; _i++) {
var param = _a[_i];
this.localTypes[i] = param.typeName ? [param.typeName] : [];
i++;
}
};
CompilerState.prototype.evalStackIndex = function (stackOffset) {
var stack = this.currentOpcode.stack;
var mapped = (stack - 1 - stackOffset);
if (mapped < 0)
return -1;
return mapped;
};
CompilerState.prototype.moveIndent = function (offset) {
this._indentLen += offset * 4;
if (this._indentLen < 0)
this._indentLen = 0;
this._indent = (' ').repeat(this._indentLen);
return this._indent;
};
CompilerState.prototype.getMultinameIndex = function (nameOrIndex) {
var name = typeof nameOrIndex === 'number'
? this.abc.getMultiname(nameOrIndex)
: nameOrIndex;
var index = this.names.indexOf(name);
if (index > -1)
return index;
this.names.push(name);
return this.names.length - 1;
};
/**
* Emit constant assigment, and store it in alias tree
* @param stackIndex
* @param value
* @param isConst - value real primitive const value, not a const alias
*/
CompilerState.prototype.emitConst = function (stackIndex, value, isConst) {
if (isConst === void 0) { isConst = true; }
var real = this.evalStackIndex(stackIndex);
var name = emitInlineStack(this, stackIndex, false);
this.popAnyAlias(name);
if (Settings.UNSAFE_INLINE_CONST) {
this.constAliases[name] = {
value: value,
kind: isConst ? "const" /* VAR_KIND.CONST */ : "alias" /* VAR_KIND.ALIAS */,
pos: this.mainBlock.length
};
this.stackAliases[real] = this.constAliases[name];
}
if (isConst && typeof value === 'string')
value = JSON.stringify(value);
return this.mainBlock.push(this.indent + name + ' = ' + value + ';');
};
CompilerState.prototype.emitGetLocal = function (stackIndex, localIndex) {
var stack = emitInlineStack(this, stackIndex, false);
this.popAnyAlias(stack);
// local 0 is ALWAYS THIS
if (localIndex === 0) {
this.pushThisAlias(stack);
}
if (Settings.UNSAFE_INLINE_CONST) {
var local = 'local' + localIndex;
this.constAliases[stack] = { value: local, pos: this.mainBlock.length, kind: "alias" /* VAR_KIND.ALIAS */ };
this.localAliases[local] = stack;
}
if (this.localTypes[localIndex] && this.localTypes[localIndex][0]) {
this.emitMain('// JIT: potential type:' + this.localTypes[localIndex][0].toString());
}
return this.mainBlock.push(this.indent + stack + ' = ' + emitInlineLocal(this, localIndex) + ';');
};
/**
* Push line to main code block and prepend indent automatically
* @param line Line to emit to generated code
* @returns line count
*/
CompilerState.prototype.emitMain = function (line) {
if (line === void 0) { line = ''; }
return this.mainBlock.push(this.indent + line);
};
/**
* Push line to head code block WITHOUT ident, because it not track it
* @param line Line to emit to generated code
* @returns line count
*/
CompilerState.prototype.emitHead = function (line, indent) {
if (indent === void 0) { indent = ''; }
return this.headerBlock.push(indent + line);
};
/**
* Emit block begin ({) and move indent right
* @param value string that was emited instead of {
*/
CompilerState.prototype.emitBeginMain = function (value) {
if (value === void 0) { value = '{'; }
var pos = this.emitMain(value);
this.moveIndent(1);
return pos;
};
/**
* Emit block end } and move indent left
*/
CompilerState.prototype.emitEndMain = function () {
this.moveIndent(-1);
var pos = this.emitMain('}');
return pos;
};
CompilerState.prototype.killConstAliasInstruction = function (aliases) {
if (!aliases || aliases.length === 0)
return;
for (var _i = 0, aliases_1 = aliases; _i < aliases_1.length; _i++) {
var a = aliases_1[_i];
if (this.constAliases[a]) {
var instr = this.mainBlock[this.constAliases[a].pos];
this.mainBlock[this.constAliases[a].pos] = '//' + instr + '// JIT: redundant assigment, value unused';
}
}
};
CompilerState.prototype.getConstAliasMeta = function (stackOffset) {
if (!Settings.UNSAFE_INLINE_CONST)
return null;
return this.constAliases['stack' + this.evalStackIndex(stackOffset)];
};
CompilerState.prototype.getConstAlias = function (alias) {
if (!Settings.UNSAFE_INLINE_CONST)
return alias;
var val = this.constAliases[alias];
if (!val)
return alias;
// we should don't map value for this, because maybe a fast mapping
if (val.kind !== "const" /* VAR_KIND.CONST */) {
return val.value;
}
if (typeof val.value === 'string') {
return JSON.stringify(val.value);
}
return '' + val.value;
};
CompilerState.prototype.isThisAlias = function (alias) {
if (!Settings.UNSAFE_PROPAGATE_THIS)
return false;
return this.thisAliases.has(alias);
};
CompilerState.prototype.pushThisAlias = function (alias, from) {
if (!Settings.UNSAFE_PROPAGATE_THIS)
return false;
if (from && !this.thisAliases.has(from))
return false;
if (this.thisAliases.has(alias))
return false;
this.thisAliases.add(alias);
return true;
};
CompilerState.prototype.dropAllAliases = function () {
this.constAliases = {};
this.localAliases = {};
};
CompilerState.prototype.popAnyAlias = function (stackOrLocal) {
// remove back referenced alias for local
if (stackOrLocal in this.localAliases) {
var l = stackOrLocal;
stackOrLocal = this.localAliases[l];
delete this.localAliases[l];
}
//remove and const alias, reassigment
delete this.constAliases[stackOrLocal];
return this.thisAliases.delete(stackOrLocal);
};
return CompilerState;
}());
export { CompilerState };