UNPKG

@awayfl/avm2

Version:

Virtual machine for executing AS3 code

343 lines (342 loc) 14.4 kB
import { Multiname } from '../abc/lazy/Multiname'; import { release, isNumeric, defineNonEnumerableProperty } from '@awayfl/swf-loader'; import { addPrototypeFunctionAlias } from './addPrototypeFunctionAlias'; import { checkValue } from '../run/checkValue'; import { makeMultiname } from './makeMultiname'; import { axCoerceString } from '../run/axCoerceString'; import { qualifyPublicName } from '../nat/qualifyPublicName'; import { axCoerceName } from '../run/axCoerceName'; import { assert } from '@awayjs/graphics'; import { rn } from './rn'; import { Errors } from '../errors'; import { validateCall } from '../run/validateCall'; import { validateConstruct } from '../run/validateConstruct'; var ASObject = /** @class */ (function () { function ASObject() { } ASObject.classInitializer = function () { var proto = this.dPrototype; var asProto = ASObject.prototype; addPrototypeFunctionAlias(proto, '$BghasOwnProperty', asProto.native_hasOwnProperty); addPrototypeFunctionAlias(proto, '$BgpropertyIsEnumerable', asProto.native_propertyIsEnumerable); addPrototypeFunctionAlias(proto, '$BgsetPropertyIsEnumerable', asProto.native_setPropertyIsEnumerable); addPrototypeFunctionAlias(proto, '$BgisPrototypeOf', asProto.native_isPrototypeOf); addPrototypeFunctionAlias(proto, '$BgtoLocaleString', asProto.toString); }; /* constructor() { // To prevent accidental instantiation of template classes, make sure that we throw // right during construction. release;// 80pro: this errors when instancing our Loader class: || checkValue(this); } */ ASObject._init = function () { // Nop. }; ASObject.init = function () { // Nop. }; ASObject.prototype.native_isPrototypeOf = function (v) { return this.isPrototypeOf(this.sec.box(v)); }; ASObject.prototype.native_hasOwnProperty = function (nm) { return this.axHasOwnProperty(makeMultiname(nm)); }; ASObject.prototype.native_propertyIsEnumerable = function (nm) { var descriptor = Object.getOwnPropertyDescriptor(this, qualifyPublicName(axCoerceString(nm))); return !!descriptor && descriptor.enumerable; }; ASObject.prototype.native_setPropertyIsEnumerable = function (nm, enumerable) { if (enumerable === void 0) { enumerable = true; } var qualifiedName = qualifyPublicName(axCoerceString(nm)); enumerable = !!enumerable; var instanceInfo = this.axClass.classInfo.instanceInfo; if (instanceInfo.isSealed() && this !== this.axClass.dPrototype) { this.sec.throwError('ReferenceError', Errors.WriteSealedError, nm, instanceInfo.multiname.name); } // Silently ignore trait properties. var descriptor = Object.getOwnPropertyDescriptor(this.axClass.tPrototype, qualifiedName); if (descriptor && this !== this.axClass.dPrototype) { return; } descriptor = Object.getOwnPropertyDescriptor(this, qualifiedName); // ... and non-existent properties. if (!descriptor) { return; } if (descriptor.enumerable !== enumerable) { descriptor.enumerable = enumerable; Object.defineProperty(this, qualifiedName, descriptor); } }; ASObject.prototype.axResolveMultiname = function (mn) { if (mn.numeric) return mn.numericValue; var s = mn.name; if (mn.mutable) { var t = this.traits.getTrait(mn.namespaces, s); return t ? t.multiname.getMangledName() : '$Bg' + s; } else { var c = mn.resolved[this.axClassName]; if (c) return c; var t = this.traits.getTraitMultiname(mn); var r = t ? t.multiname.getMangledName() : ('$Bg' + s); mn.resolved[this.axClassName] = r; return r; } }; ASObject.prototype.axHasProperty = function (mn) { return this.axHasPropertyInternal(mn); }; ASObject.prototype.axHasPublicProperty = function (nm) { rn.set(nm); var result = this.axHasProperty(rn); //release || assert(rn.name === nm || isNaN(rn.name) && isNaN(nm)); rn.drop(); return result; }; ASObject.prototype.axSetProperty = function (mn, value, bc) { //if(typeof value == "number" && isNaN(value)) // console.log("try to set NaN", mn); //release || checkValue(value); var name = mn.name; if (typeof name === 'number' || isNumeric(name = axCoerceName(name))) { //release || assert(mn.isRuntimeName()); this[+name] = value; return; } var freeze = false; var t = this.traits.getTrait(mn.namespaces, name); var mangledName; if (t) { mangledName = t.multiname.getMangledName(); switch (t.kind) { case 1 /* TRAIT.Method */: this.sec.throwError('ReferenceError', Errors.CannotAssignToMethodError, name, this.axClass.name.name); // Unreachable because of throwError. break; case 2 /* TRAIT.Getter */: this.sec.throwError('ReferenceError', Errors.ConstWriteError, name, this.axClass.name.name); // Unreachable because of throwError. break; case 4 /* TRAIT.Class */: case 6 /* TRAIT.Const */: // Technically, we need to check if the currently running function is the // initializer of whatever class/package the property is initialized on. // In practice, we freeze the property after first assignment, causing // an internal error to be thrown if it's being initialized a second time. // Invalid bytecode could leave out the assignent during first initialization, // but it's hard to see how that could convert into real-world problems. if (bc !== 104 /* Bytecode.INITPROPERTY */) { this.sec.throwError('ReferenceError', Errors.ConstWriteError, name, this.axClass.name.name); } freeze = true; break; } var type = t.getType(); if (type) value = type.axCoerce(value); } else { mangledName = '$Bg' + name; } this[mangledName] = value; if (freeze) { Object.defineProperty(this, mangledName, { writable: false }); } }; ASObject.prototype.axGetProperty = function (mn) { var name = this.axResolveMultiname(mn); var value = this[name]; if (typeof value === 'function' && !value.__isClosure) return this.axGetMethod(name); //80pro: workaround: if (typeof value === 'undefined' && typeof name !== 'number') { return this[mn.name]; } //release || checkValue(value); return value; }; ASObject.prototype.axGetMethod = function (name) { release || assert(typeof this[name] === 'function'); var cache = this._methodClosureCache; if (!cache) { Object.defineProperty(this, '_methodClosureCache', { value: Object.create(null) }); cache = this._methodClosureCache; } var method = cache[name]; if (!method) { method = cache[name] = this.sec.AXMethodClosure.Create(this, this[name]); } return method; }; ASObject.prototype.axGetSuper = function (mn, scope) { var name = axCoerceName(mn.name); var namespaces = mn.namespaces; var trait = scope.parent.object.tPrototype.traits.getTrait(namespaces, name); var value; if (trait.kind === 2 /* TRAIT.Getter */ || trait.kind === 7 /* TRAIT.GetterSetter */) { value = trait.get.call(this); } else { var mangledName = trait.multiname.getMangledName(); var fun = scope.parent.object.tPrototype[mangledName]; if (fun) return this.sec.AXMethodClosure.Create(this, fun); } release || checkValue(value); return value; }; ASObject.prototype.axSetSuper = function (mn, scope, value) { release || checkValue(value); var name = axCoerceName(mn.name); var namespaces = mn.namespaces; var trait = scope.parent.object.tPrototype.traits.getTrait(namespaces, name); var type = trait.getType(); if (type) { value = type.axCoerce(value); } if (trait.kind === 3 /* TRAIT.Setter */ || trait.kind === 7 /* TRAIT.GetterSetter */) { trait.set.call(this, value); } else { this[trait.multiname.getMangledName()] = value; } }; ASObject.prototype.axDeleteProperty = function (mn) { // Cannot delete traits. var name = axCoerceName(mn.name); var namespaces = mn.namespaces; if (this.traits.getTrait(namespaces, name)) { return false; } return delete this[mn.getPublicMangledName()]; }; ASObject.prototype.axCallProperty = function (mn, args, isLex) { var fun = this[this.axResolveMultiname(mn)]; //console.log("call function name:", name); validateCall(this.sec, fun, args.length); return fun.axApply(isLex ? null : this, args); }; ASObject.prototype.axCallSuper = function (mn, scope, args) { var fun = scope.parent.object.tPrototype[this.axResolveMultiname(mn)]; validateCall(this.sec, fun, args.length); return fun.axApply(this, args); }; ASObject.prototype.axConstructProperty = function (mn, args) { var ctor = this[this.axResolveMultiname(mn)]; validateConstruct(this.sec, ctor, args.length); return ctor.axConstruct(args); }; ASObject.prototype.axHasPropertyInternal = function (mn) { var key = this.axResolveMultiname(mn); return key in this; }; ASObject.prototype.axHasOwnProperty = function (mn) { var name = this.axResolveMultiname(mn); // We have to check for trait properties too if a simple hasOwnProperty fails. // This is different to JavaScript's hasOwnProperty behaviour where hasOwnProperty returns // false for properties defined on the property chain and not on the instance itself. return this.hasOwnProperty(name) || this.axClass.tPrototype.hasOwnProperty(name); }; ASObject.prototype.axGetEnumerableKeys = function () { if (this.sec.isPrimitive(this)) { return []; } var tPrototype = Object.getPrototypeOf(this); var keys = Object.keys(this); var result = []; for (var i = 0; i < keys.length; i++) { var key = keys[i]; if (isNumeric(key)) { result.push(key); } else { if (tPrototype.hasOwnProperty(key)) { continue; } var name_1 = Multiname.stripPublicMangledName(key); if (name_1 !== undefined) { result.push(name_1); } } } return result; }; ASObject.prototype.axGetPublicProperty = function (nm) { return this[Multiname.getPublicMangledName(nm)]; }; ASObject.prototype.axSetPublicProperty = function (nm, value) { release || checkValue(value); this[Multiname.getPublicMangledName(nm)] = value; }; ASObject.prototype.axCallPublicProperty = function (nm, argArray) { return this[Multiname.getPublicMangledName(nm)].axApply(this, argArray); }; ASObject.prototype.axDeletePublicProperty = function (nm) { return delete this[Multiname.getPublicMangledName(nm)]; }; ASObject.prototype.axGetSlot = function (i) { var t = this.traits.getSlot(i); var value = this[t.multiname.getMangledName()]; release || checkValue(value); return value; }; ASObject.prototype.axSetSlot = function (i, value) { release || checkValue(value); var t = this.traits.getSlot(i); var name = t.multiname.getMangledName(); var type = t.getType(); this[name] = type ? type.axCoerce(value) : value; }; /** * Gets the next name index of an object. Index |zero| is actually not an * index, but rather an indicator to start the iteration. */ ASObject.prototype.axNextNameIndex = function (index) { var self = this; if (index === 0) { // Gather all enumerable keys since we're starting a new iteration. defineNonEnumerableProperty(self, 'axEnumerableKeys', self.axGetEnumerableKeys()); } rn.drop(); var axEnumerableKeys = self.axEnumerableKeys; while (index < axEnumerableKeys.length) { var key = axEnumerableKeys[index]; rn.set(key); if (self.axHasPropertyInternal(rn)) { release || assert(rn.name === axEnumerableKeys[index]); return index + 1; } index++; } return 0; }; /** * Gets the nextName after the specified |index|, which you would expect to * be index + 1, but it's actually index - 1; */ ASObject.prototype.axNextName = function (index) { var self = this; var axEnumerableKeys = self.axEnumerableKeys; release || assert(axEnumerableKeys && index > 0 && index < axEnumerableKeys.length + 1); return axEnumerableKeys[index - 1]; }; ASObject.prototype.axNextValue = function (index) { return this.axGetPublicProperty(this.axNextName(index)); }; ASObject.prototype.axSetNumericProperty = function (nm, value) { this.axSetPublicProperty(nm, value); }; ASObject.prototype.axGetNumericProperty = function (nm) { return this.axGetPublicProperty(nm); }; ASObject.forceNativeConstructor = false; ASObject.forceNativeMethods = false; ASObject.classSymbols = null; ASObject.instanceSymbols = null; return ASObject; }()); export { ASObject };