UNPKG

@awayfl/avm2

Version:

Virtual machine for executing AS3 code

236 lines (235 loc) 9 kB
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 };