@awayfl/avm2
Version:
Virtual machine for executing AS3 code
134 lines (121 loc) • 3.68 kB
text/typescript
import { RuntimeTraitInfo } from './RuntimeTraitInfo';
import { Namespace } from './Namespace';
import { release } from '@awayfl/swf-loader';
import { assert } from '@awayjs/graphics';
import { NamespaceType } from './NamespaceType';
import { Multiname } from './Multiname';
import { TRAIT } from './TRAIT';
export class RuntimeTraits {
public slots: RuntimeTraitInfo[] = [];
private _traits: Record<string, Record<string, RuntimeTraitInfo>>;
private _nextSlotID: number = 1;
constructor(
public readonly superTraits: RuntimeTraits,
public readonly protectedNs: Namespace,
public readonly protectedNsMappings: Record <string, RuntimeTraitInfo>
) {
const traits = this._traits = Object.create(null);
if (!superTraits) {
return;
}
const superMappings = superTraits._traits;
for (const key in superMappings) {
traits[key] = Object.create(superMappings[key]);
}
}
/**
* Adds the given trait and returns any trait that might already exist under that name.
*
* See the comment for `Trait#resolveRuntimeTraits` for an explanation of the lookup scheme.
*/
public addTrait(trait: RuntimeTraitInfo): RuntimeTraitInfo {
const mn = trait.multiname;
let mappings = this._traits[mn.name];
if (!mappings) {
mappings = this._traits[mn.name] = Object.create(null);
}
const nsName = mn.namespaces[0].mangledName;
const current = mappings[nsName];
mappings[nsName] = trait;
return current;
}
public addSlotTrait(trait: RuntimeTraitInfo) {
let slot = trait.slot;
if (!slot) {
slot = trait.slot = this._nextSlotID++;
} else {
this._nextSlotID = slot + 1;
}
release || assert(!this.slots[slot]);
this.slots[slot] = trait;
}
private multinames: object = {}
getTraitMultiname(mn: Multiname): RuntimeTraitInfo {
if (mn.mutable)
return this.getTrait(mn.namespaces, mn.name);
return this.multinames[mn.id] || (this.multinames[mn.id] = this.getTrait(mn.namespaces, mn.name));
}
/**
* Returns the trait matching the given multiname parts, if any.
*
* See the comment for `Trait#resolveRuntimeTraits` for an explanation of the lookup scheme.
*/
getTrait(namespaces: Namespace[], name: string): RuntimeTraitInfo {
release || assert(typeof name === 'string');
const mappings = this._traits[name];
if (!mappings) {
return null;
}
let trait: RuntimeTraitInfo;
for (let i = 0; i < namespaces.length; i++) {
const ns = namespaces[i];
trait = mappings[ns.mangledName];
if (trait) {
return trait;
}
if (ns.type === NamespaceType.Protected) {
let protectedScope: RuntimeTraits = this;
while (protectedScope) {
if (protectedScope.protectedNs === ns) {
trait = protectedScope.protectedNsMappings[name];
if (trait) {
return trait;
}
}
protectedScope = protectedScope.superTraits;
}
}
}
return null;
}
public getTraitsList(): RuntimeTraitInfo[] {
const list: RuntimeTraitInfo[] = [];
const names = this._traits;
for (const name in names) {
const mappings = names[name];
for (const nsName in mappings) {
list.push(mappings[nsName]);
}
}
return list;
}
public getPublicTraitNames(): string [] {
const list: string[] = [];
const names = this._traits;
let trait: RuntimeTraitInfo;
for (const name in names) {
const mappings = names[name];
for (const nsName in mappings) {
trait = mappings[nsName];
if (trait.multiname.namespace.isPublic() &&
(trait.kind == TRAIT.Slot || trait.kind == TRAIT.GetterSetter)) {
list.push(trait.multiname.name);
}
}
}
return list;
}
getSlot(i: number): RuntimeTraitInfo {
return this.slots[i];
}
}