@awayfl/avm1
Version:
Virtual machine for executing AS1 and AS2 code
202 lines (182 loc) • 5.54 kB
text/typescript
/*
* Copyright 2014 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//module Shumway.AVM1 {
import { ActionCode, ActionsDataParser, ParsedAction } from './parser';
export interface ActionCodeBlock {
label: number;
items: ActionCodeBlockItem[];
jump: number;
}
export interface ActionItemFlags {
optimised?: boolean;
killed?: boolean;
}
export interface ActionCodeBlockItem {
action: ParsedAction;
next: number;
conditionalJumpTo: number;
flags?: ActionItemFlags;
}
export interface AnalyzerResults {
/** Sparsed array with compiled actions, index is an original location
* in the binary actions data */
actions: ActionCodeBlockItem[];
blocks: ActionCodeBlock[];
dataId: string;
singleConstantPool: any[];
registersLimit: number /* int */;
}
export class ActionsDataAnalyzer {
public parentResults: AnalyzerResults = null;
public registersLimit: number /* int */ = 0;
constructor() {}
analyze(parser: ActionsDataParser): AnalyzerResults {
const actions: ActionCodeBlockItem[] = [];
const labels: number[] = [0];
const processedLabels: boolean[] = [true];
let constantPoolFound: boolean = false;
let singleConstantPoolAt0: any[] = null;
// Parsing all actions we can reach. Every action will have next position
// and conditional jump location.
const queue: number[] = [0];
let position;
let action: ParsedAction;
let nextPosition;
let item: ActionCodeBlockItem;
let jumpPosition: number;
let branching: boolean;
let nonConditionalBranching: boolean;
let skipCount: number;
while (queue.length > 0) {
position = queue.shift();
if (actions[position]) {
continue;
}
parser.position = position;
// reading block of actions until the first jump of end of actions
while (!parser.eof && !actions[position]) {
action = parser.readNext();
if (action.actionCode === 0) {
break;
}
nextPosition = parser.position;
item = {
action: action,
next: nextPosition,
conditionalJumpTo: -1
};
jumpPosition = 0;
branching = false;
nonConditionalBranching = false;
switch (action.actionCode) {
case ActionCode.ActionWaitForFrame:
case ActionCode.ActionWaitForFrame2:
branching = true;
// skip is specified in amount of actions (instead of bytes)
skipCount = action.actionCode === ActionCode.ActionWaitForFrame ?
action.args[1] : action.args[0];
parser.skip(skipCount);
jumpPosition = parser.position;
parser.position = nextPosition;
break;
case ActionCode.ActionJump:
nonConditionalBranching = true;
branching = true;
jumpPosition = nextPosition + action.args[0];
break;
case ActionCode.ActionIf:
branching = true;
jumpPosition = nextPosition + action.args[0];
break;
case ActionCode.ActionThrow:
case ActionCode.ActionReturn:
case ActionCode.None:
nonConditionalBranching = true;
branching = true;
jumpPosition = parser.length;
break;
case ActionCode.ActionConstantPool:
if (constantPoolFound) {
singleConstantPoolAt0 = null; // reset if more than one found
break;
}
constantPoolFound = true;
if (position === 0) {
// For now only counting at position 0 of the block of actions
singleConstantPoolAt0 = action.args[0];
}
break;
}
if (branching) {
if (nonConditionalBranching) {
item.next = jumpPosition;
} else {
item.conditionalJumpTo = jumpPosition;
}
if (!processedLabels[jumpPosition]) {
labels.push(jumpPosition);
queue.push(jumpPosition);
processedLabels[jumpPosition] = true;
}
}
actions[position] = item;
if (nonConditionalBranching) {
break;
}
position = nextPosition;
}
}
// Creating blocks for every unique label
const blocks: ActionCodeBlock[] = [];
let items: ActionCodeBlockItem[];
let lastPosition;
labels.forEach((position) => {
if (!actions[position]) {
return;
}
items = [];
lastPosition = position;
let item;
// continue grabbing items until other label or next code exist
do {
item = actions[lastPosition];
items.push(item);
lastPosition = item.next;
} while (!processedLabels[lastPosition] && actions[lastPosition]);
blocks.push({
label: position,
items: items,
jump: lastPosition
});
});
// Determines if action blocks (or defined function) is using the single
// constants pool defined at the beginning of the action block.
let singleConstantPool: any[] = null;
if (constantPoolFound) {
singleConstantPool = singleConstantPoolAt0;
} else if (this.parentResults) {
// Trying to use parent's constant pool if available.
singleConstantPool = this.parentResults.singleConstantPool;
}
return {
actions: actions,
blocks: blocks,
dataId: parser.dataId,
singleConstantPool: singleConstantPool,
registersLimit: this.registersLimit
};
}
}