/** * traverse the delegate chain looking for a value. * [ed] it's okay to look only at the HT's in the delegate chain because * delegate values may only be instances of Object. They cannot be objects * with slots. We don't need to look at traits at each step. * todo - enforce this rule * @param name * @return */ Atom ScriptObject::getAtomProperty(Atom name) const { if (!traits()->needsHashtable()) { return getAtomPropertyFromProtoChain(name, delegate, traits()); } else { Stringp s = core()->atomToString(name); AvmAssert(s->isInterned()); Atom ival = s->getIntAtom(); if (ival) { name = ival; } // dynamic lookup on this object const ScriptObject *o = this; do { // ensure prototype is dynamic if (!o->vtable->traits->getHashtableOffset()) continue; Atom const value = o->getTable()->getNonEmpty(name); if (!InlineHashtable::isEmpty(value)) return value; } while ((o = o->delegate) != NULL); return undefinedAtom; } }
Atom ScriptObject::getUintProperty(uint32_t i) const { // N.B.: a key present in ScriptObject must be interned string; // thus uninterned implies absent (cf. bugzilla 556023). AvmCore* core = this->core(); if (!(i&MAX_INTEGER_MASK)) { if (!traits()->needsHashtable()) { Stringp interned; bool present = core->isInternedUint(i, &interned); if (present) { Atom name = interned->atom(); return getAtomPropertyFromProtoChain(name, delegate, traits()); } else { return undefinedAtom; } } else { // dynamic lookup on this object Atom name = core->uintToAtom (i); const ScriptObject *o = this; do { // ensure prototype is dynamic if (!o->vtable->traits->getHashtableOffset()) continue; Atom const value = o->getTable()->getNonEmpty(name); if (!InlineHashtable::isEmpty(value)) return value; } while ((o = o->delegate) != NULL); return undefinedAtom; } } else { Stringp interned; bool present; present = core->isInternedUint(i, &interned); if (present) { return getAtomProperty(interned->atom()); } else { return undefinedAtom; } } }
STRATEGY* JlsCodecFactory<STRATEGY>::GetCodecImpl(const JlsParamaters& _info) { if (_info.ilv == ILV_SAMPLE && _info.components != 3) return NULL; #ifndef DISABLE_SPECIALIZATIONS // optimized lossless versions common formats if (_info.allowedlossyerror == 0) { if (_info.ilv == ILV_SAMPLE) { if (_info.bitspersample == 8) return new JlsCodec<LosslessTraitsT<Triplet<BYTE>,8>, STRATEGY>(LosslessTraitsT<Triplet<BYTE>,8>(), _info); } else { switch (_info.bitspersample) { case 8: return new JlsCodec<LosslessTraitsT<BYTE, 8>, STRATEGY>(LosslessTraitsT<BYTE, 8>(), _info); case 12: return new JlsCodec<LosslessTraitsT<USHORT,12>, STRATEGY>(LosslessTraitsT<USHORT, 12>(), _info); case 16: return new JlsCodec<LosslessTraitsT<USHORT,16>, STRATEGY>(LosslessTraitsT<USHORT, 16>(), _info); } } } #endif int maxval = (1 << _info.bitspersample) - 1; if (_info.bitspersample <= 8) { if (_info.ilv == ILV_SAMPLE) { DefaultTraitsT<BYTE,Triplet<BYTE> > traits(maxval, _info.allowedlossyerror); return new JlsCodec<DefaultTraitsT<BYTE,Triplet<BYTE> >, STRATEGY>(traits, _info); } DefaultTraitsT<BYTE, BYTE> traits((1 << _info.bitspersample) - 1, _info.allowedlossyerror); return new JlsCodec<DefaultTraitsT<BYTE, BYTE>, STRATEGY>(traits, _info); } else if (_info.bitspersample <= 16) { if (_info.ilv == ILV_SAMPLE) { DefaultTraitsT<USHORT,Triplet<USHORT> > traits(maxval, _info.allowedlossyerror); return new JlsCodec<DefaultTraitsT<USHORT,Triplet<USHORT> >, STRATEGY>(traits, _info); } DefaultTraitsT<USHORT, USHORT> traits(maxval, _info.allowedlossyerror); return new JlsCodec<DefaultTraitsT<USHORT, USHORT>, STRATEGY>(traits, _info); } return NULL; }
bool ArrayObject::delUintProperty(uint32 index) { // if we get here, we have a valid integer index. if (traits()->needsHashtable()) { if ((index < getDenseLength())) { if (index == (getDenseLength() - 1)) { m_denseArr.pop(); } // We're deleting an element in the middle of our array. The lower // part can be left in the dense array but the upper part needs to // get moved to the HT. else { for (uint32 i = index + 1; i < getDenseLength(); i++) { ScriptObject::setUintProperty (i, m_denseArr.getAtFast(i)); } m_denseArr.splice (index, 0, (getDenseLength() - index), 0); } return true; } } return ScriptObject::delUintProperty(index); }
NamespaceClass::NamespaceClass(VTable* cvtable) : ClassClosure(cvtable) { toplevel()->_namespaceClass = this; AvmAssert(traits()->getSizeOfInstance() == sizeof(NamespaceClass)); createVanillaPrototype(); }
// this = argv[0] (ignored) // arg1 = argv[1] // argN = argv[argc] Atom RegExpClass::construct(int argc, Atom* argv) { AvmCore* core = this->core(); Stringp pattern; Atom patternAtom = (argc>0) ? argv[1] : undefinedAtom; Atom optionsAtom = (argc>1) ? argv[2] : undefinedAtom; if (AvmCore::istype(patternAtom, traits()->itraits)) { // Pattern is a RegExp object if (optionsAtom != undefinedAtom) { // ECMA 15.10.4.1 says to throw an error if flags specified toplevel()->throwTypeError(kRegExpFlagsArgumentError); } // Return a clone of the RegExp object RegExpObject* regExpObject = (RegExpObject*)AvmCore::atomToScriptObject(patternAtom); return (new (core->GetGC(), ivtable()->getExtraSize()) RegExpObject(regExpObject))->atom(); } else { if (patternAtom != undefinedAtom) { pattern = core->string(argv[1]); } else { // cn: disable this, breaking ecma3 tests. was: todo look into this. it's what SpiderMonkey does. pattern = core->kEmptyString; //core->newConstantStringLatin1("(?:)"); } } Stringp options = NULL; if (optionsAtom != undefinedAtom) { options = core->string(optionsAtom); } RegExpObject* inst = new (core->GetGC(), ivtable()->getExtraSize()) RegExpObject(this, pattern, options); return inst->atom(); }
TEST(ObjectSerializationTest, DynamicSealedAnonymousObject) { AmfObjectTraits traits("", true, false); AmfObject obj(traits); obj.addSealedProperty("sealedProp", AmfString("value")); obj.addDynamicProperty("dynamicProp", AmfString("dynamicValue")); isEqual(v8 { 0x0a, // AMF_OBJECT 0x1b, // 0b11011, U29O-traits, dynamic, 1 sealed property 0x01, // class-name "" (anonymous object) // sealed property names // UTF-8-vr "sealedProp" 0x15, 0x73, 0x65, 0x61, 0x6c, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x70, // sealed property values // AmfString "value" 0x06, 0x0b, 0x76, 0x61, 0x6c, 0x75, 0x65, // dynamic members // UTF-8-vr "dynamicProp" 0x17, 0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x50, 0x72, 0x6f, 0x70, // AmfString "dynamicValue" 0x06, 0x19, 0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, // end of dynamic members 0x01 }, obj); }
// Execute the ToString algorithm as described in ECMA-262 Section 9.8. // This is ToString(ToPrimitive(input argument, hint String)) // ToPrimitive(input argument, hint String) calls [[DefaultValue]] // described in ECMA-262 8.6.2.6. The [[DefaultValue]] algorithm // with hint String is inlined here. Stringp ScriptObject::toString() { AvmCore *core = this->core(); Toplevel* toplevel = this->toplevel(); Atom atomv_out[1]; // call this.toString() // NOTE use callers versioned public to get correct toString Multiname tempname(core->findPublicNamespace(), core->ktoString); atomv_out[0] = atom(); Atom result = toplevel->callproperty(atom(), &tempname, 0, atomv_out, vtable); // if result is primitive, return its ToString if (atomKind(result) != kObjectType) return core->string(result); // otherwise call this.valueOf() tempname.setName(core->kvalueOf); atomv_out[0] = atom(); result = toplevel->callproperty(atom(), &tempname, 0, atomv_out, vtable); // if result is primitive, return it if (atomKind(result) != kObjectType) return core->string(result); // could not convert to primitive. toplevel->throwTypeError(kConvertToPrimitiveError, core->toErrorString(traits())); return NULL; // unreachable }
TEST(ObjectSerializationTest, DynamicSealedNamedObject) { AmfObjectTraits traits("de.ventero.AmfTest", true, false); AmfObject obj(traits); obj.addSealedProperty("sealedProp", AmfDouble(3.14159)); obj.addDynamicProperty("dynamicProp", AmfInteger(17)); isEqual(v8 { 0x0a, // AMF_OBJECT 0x1b, // 0b11011, U29O-traits, dynamic, 1 sealed property // class-name UTF-8-vr "de.ventero.AmfTest" 0x25, 0x64, 0x65, 0x2e, 0x76, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x6f, 0x2e, 0x41, 0x6d, 0x66, 0x54, 0x65, 0x73, 0x74, // sealed property names // UTF-8-vr "sealedProp" 0x15, 0x73, 0x65, 0x61, 0x6c, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x70, // sealed property values // AmfDouble 3.14159 0x05, 0x40, 0x09, 0x21, 0xf9, 0xf0, 0x1b, 0x86, 0x6e, // dynamic members // UTF-8-vr dynamicProp 0x17, 0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x50, 0x72, 0x6f, 0x70, // AmfInteger 17 0x04, 0x11, // end of dynamic members 0x01 }, obj); }
ClassClosure* FunctionClass::createEmptyFunction() { // invoke AS3 private static function emptyCtor, which returns an empty function. TraitsBindingsp t = traits()->getTraitsBindings(); Binding b = t->findBinding(core()->internConstantStringLatin1("emptyCtor")); MethodEnv *f = vtable->methods[AvmCore::bindingToMethodId(b)]; return (ClassClosure*)AvmCore::atomToScriptObject(f->coerceEnter(this->atom())); }
Atom ScriptObject::getMultinameProperty(const Multiname* multiname) const { if (multiname->isValidDynamicName()) { return getStringProperty(multiname->getName()); } else { Toplevel* toplevel = this->toplevel(); if (multiname->isNsset()) toplevel->throwReferenceError(kReadSealedErrorNs, multiname, traits()); else toplevel->throwReferenceError(kReadSealedError, multiname, traits()); return undefinedAtom; } }
TEST(ObjectSerializationTest, SealedAnonymousObject) { { AmfObjectTraits traits("", false, false); AmfObject obj(traits); obj.addSealedProperty("sealedProp", AmfString("value")); isEqual(v8 { 0x0a, // AMF_OBJECT 0x13, // 0b10011, U29O-traits, not dynamic, 1 sealed property 0x01, // class-name "" (anonymous object) // sealed property names // UTF-8-vr "sealedProp" 0x15, 0x73, 0x65, 0x61, 0x6c, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x70, // sealed property values // AmfString "value" 0x06, 0x0b, 0x76, 0x61, 0x6c, 0x75, 0x65 // no dynamic members, so no empty string }, obj); } { AmfObjectTraits traits("", false, false); AmfObject obj(traits); obj.addSealedProperty("sealedProp", AmfString("value")); obj.addSealedProperty("otherSealedProp", AmfString("otherValue")); isEqual(v8 { 0x0a, // AMF_OBJECT 0x23, // 0b100011, U29O-traits, not dynamic, 2 sealed properties 0x01, // class-name "" (anonymous object) // sealed property names // UTF-8-vr "sealedProp" 0x15, 0x73, 0x65, 0x61, 0x6c, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x70, // UTF-8-vr "otherSealedProp" 0x1f, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x53, 0x65, 0x61, 0x6c, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x70, // sealed propety values // AmfString "value" 0x06, 0x0b, 0x76, 0x61, 0x6c, 0x75, 0x65, // AmfString "otherValue" 0x06, 0x15, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65 // no dynamic members }, obj); } }
MathClass::MathClass(VTable* cvtable) : ClassClosure(cvtable) { AvmAssert(traits()->getSizeOfInstance() == sizeof(MathClass)); MathUtils::initRandom(&seed); // todo does ES4 Math have a prototype object? }
/* The storage for a ScriptObject or a subclass SO of ScriptObject is * laid out as follows. * * - first the bits of the C++ class; if there are pointers here * then they must be traced explicitly by the appropriate class's * gcTrace method * - then the bits for the ActionScript slots * - optionally an InlineHashtable for dynamic properties * * The in-line slots are native and their representation is described * by the Traits object (vtable->traits). They are not named, but named * lookup is possible by going to the Traits object, which contains * a name->slot map. * * The InlineHashtable always stores Atoms. */ bool ScriptObject::gcTrace(MMgc::GC* gc, size_t cursor) { (void)cursor; gc->TraceLocation(&vtable); gc->TraceLocation(&delegate); traits()->traceSlots(gc, this); if (traits()->needsHashtable()) { // Avoid initializing the hash table here InlineHashtable* iht = getTableNoInit(); // The iht pointer can be NULL for Dictionary objects that // haven't had init called yet. if(iht) iht->gcTrace(gc); } return false; }
int ScriptObject::nextNameIndex(int index) { AvmAssert(index >= 0); if (!traits()->needsHashtable()) return 0; return getTable()->next(index); }
ArrayObject::ArrayObject(VTable *vtable, ScriptObject* proto, uint32 capacity) : ScriptObject(vtable, proto, 0), m_denseArr(capacity) { SAMPLE_FRAME("Array", core()); AvmAssert(traits()->getSizeOfInstance() >= sizeof(ArrayObject)); m_length = 0; m_lowHTentry = NO_LOW_HTENTRY; }
void CommentHandler::handleComment(Annotator &A, Generator& generator, clang::Sema &Sema, const char *bufferStart, int commentStart, int len, clang::SourceLocation searchLocBegin, clang::SourceLocation searchLocEnd, clang::SourceLocation commentLoc) { llvm::StringRef rawString(bufferStart+commentStart, len); std::string attributes; std::string DeclRef; if ((rawString.ltrim().startswith("/**") && !rawString.ltrim().startswith("/***")) || rawString.ltrim().startswith("/*!") || rawString.ltrim().startswith("//!") || (rawString.ltrim().startswith("///") && !rawString.ltrim().startswith("////"))) #if CLANG_VERSION_MAJOR==3 && CLANG_VERSION_MINOR<=4 if (rawString.find("deprecated") == rawString.npos) // workaround crash in comments::Sema::checkDeprecatedCommand #endif { attributes = "class=\"doc\""; clang::Preprocessor &PP = Sema.getPreprocessor(); clang::comments::CommandTraits traits(PP.getPreprocessorAllocator(), clang::CommentOptions()); #if CLANG_VERSION_MAJOR==3 && CLANG_VERSION_MINOR<=4 traits.registerBlockCommand("deprecated"); // avoid typo correction leading to crash. #endif clang::comments::Lexer lexer(PP.getPreprocessorAllocator(), PP.getDiagnostics(), traits, clang::SourceLocation::getFromRawEncoding(commentStart), bufferStart + commentStart, bufferStart + commentStart + len); clang::comments::Sema sema(PP.getPreprocessorAllocator(), PP.getSourceManager(), PP.getDiagnostics(), traits, &PP); clang::comments::Parser parser(lexer, sema, PP.getPreprocessorAllocator(), PP.getSourceManager(), PP.getDiagnostics(), traits); auto fullComment = parser.parseFullComment(); CommentVisitor visitor{A, generator, traits, Sema}; visitor.visit(fullComment); DeclRef = visitor.DeclRef; } if (!DeclRef.empty()) { docs.insert({std::move(DeclRef), { rawString.str() , commentLoc }}); generator.addTag("i", attributes, commentStart, len); return; } // Try to find a matching declaration const auto &dof = decl_offsets; //is there one and one single decl in that range. auto it_before = dof.lower_bound(searchLocBegin); auto it_after = dof.upper_bound(searchLocEnd); if (it_before != dof.end() && it_after != dof.begin() && it_before == (--it_after)) { if (it_before->second.second) { docs.insert({it_before->second.first, { rawString.str() , commentLoc }}); } else { attributes %= " data-doc=\"" % it_before->second.first % "\""; } } generator.addTag("i", attributes, commentStart, len); }
TEST(ObjectSerializationTest, EmptySealedAnonymousObject) { AmfObjectTraits traits("", false, false); AmfObject obj(traits); isEqual(v8 { 0x0a, // AMF_OBJECT 0x03, // 0b0011, U29O-traits, not dynamic, 0 sealed properties 0x01 // class-name "" (anonymous object) }, obj); }
MethodClosureClass::MethodClosureClass(VTable* cvtable) : ClassClosure(cvtable) { Toplevel* toplevel = this->toplevel(); toplevel->methodClosureClass = this; AvmAssert(traits()->getSizeOfInstance() == sizeof(MethodClosureClass)); prototype = toplevel->functionClass->createEmptyFunction(); }
Atom ScriptObject::nextName(int index) { if (!traits()->needsHashtable()) return nullStringAtom; AvmAssert(index > 0); InlineHashtable* ht = getTable(); Atom m = ht->keyAt(index); return AvmCore::isNullOrUndefined(m) ? nullStringAtom : m; }
void ArrayObject::_setUintProperty(uint32 index, Atom value) { if (traits()->needsHashtable()) { if (hasDense()) { if (index == getDenseLength()) { this->m_denseArr.push (value); if (m_length < getDenseLength()) m_length = getDenseLength(); checkForSparseToDenseConversion (); return; } else if (index < getDenseLength()) { this->m_denseArr.setAt (index, value); return; } else { // fall through and put the new property into our HT } } // If we're NOT dense yet and setting first element, we can create a dense array else if (index == 0) { m_denseArr.push (value); if (!m_length) m_length = 1; else checkForSparseToDenseConversion (); return; } if (m_length <= index) { m_length = index+1; } if ((m_lowHTentry == NO_LOW_HTENTRY) || (index < m_lowHTentry)) m_lowHTentry = index; } // end if (dynamic) // If our index value is going to overflow our int atom storage and be // converted to a string, do that here instead of calling the // SciptObject::setUintProperty which will call ArrayObject::setAtomProperty // which will call back into this routine in an infinite loop. if (index & ScriptObject::MAX_INTEGER_MASK) ScriptObject::setAtomProperty(core()->internUint32(index)->atom(), value); else ScriptObject::setUintProperty(index, value); }
QNameClass::QNameClass(VTable* cvtable) : ClassClosure(cvtable) { AvmAssert(traits()->getSizeOfInstance() == sizeof(QNameClass)); createVanillaPrototype(); AvmCore* core = this->core(); kUri = core->internConstantStringLatin1("uri")->atom(); kLocalName = core->internConstantStringLatin1("localName")->atom(); }
bool ScriptObject::deleteMultinameProperty(const Multiname* name) { if (traits()->needsHashtable() && name->isValidDynamicName()) { return deleteStringProperty(name->getName()); } else { return false; } }
void ScriptObject::setMultinameProperty(const Multiname* name, Atom value) { if (traits()->needsHashtable() && name->isValidDynamicName()) { setStringProperty(name->getName(), value); } else { throwWriteSealedError(*name); } }
TEST(ObjectSerializationTest, EmptyDynamicAnonymousObject) { AmfObjectTraits traits("", true, false); AmfObject obj(traits); isEqual(v8 { 0x0a, // AMF_OBJECT 0x0b, // U29O-traits | dynamic, 0 sealed properties 0x01, // class-name "" (anonymous object) 0x01 // end of object (UTF-8-empty) }, obj); }
bool ScriptObject::hasMultinameProperty(const Multiname* multiname) const { if (traits()->needsHashtable() && multiname->isValidDynamicName()) { return hasAtomProperty(multiname->getName()->atom()); } else { // ISSUE should this walk the proto chain? return false; } }
uint64_t ScriptObject::bytesUsed() const { uint64_t bytesUsed = traits()->getTotalSize(); if(traits()->needsHashtable()) { if (traits()->isDictionary()) { union { uint8_t* p; HeapHashtable** hht; }; p = (uint8_t*)this + traits()->getHashtableOffset(); bytesUsed += (*hht)->bytesUsed(); } else { bytesUsed += getTable()->bytesUsed(); } } return bytesUsed; }
bool ArrayObject::hasUintProperty(uint32 index) const { if (traits()->needsHashtable()) { if (hasDense()) { if (index < getDenseLength()) return true; } } return ScriptObject::hasUintProperty (index); }
ObjectClass::ObjectClass(VTable* cvtable) : ClassClosure(cvtable) { toplevel()->objectClass = this; // patch Object's instance vtable scope, since we created it earlier for // bootstrapping //ivtable()->scope = cvtable->scope; AvmAssert(traits()->getSizeOfInstance() == sizeof(ObjectClass)); // it is correct call construct() when this.prototype == null prototype = construct(); }
Atom ScriptObject::nextValue(int index) { if (!traits()->needsHashtable()) return undefinedAtom; AvmAssert(index > 0); InlineHashtable* ht = getTable(); Atom m = ht->keyAt(index); if (AvmCore::isNullOrUndefined(m)) return nullStringAtom; return ht->valueAt(index); }