File: | platform/mac/avmshell/../../../core/Traits.cpp |
Location: | line 614, column 9 |
Description: | Value stored to 'nextSlotOffset' is never read |
1 | /* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ |
2 | /* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ |
3 | /* ***** BEGIN LICENSE BLOCK ***** |
4 | * Version: MPL 1.1/GPL 2.0/LGPL 2.1 |
5 | * |
6 | * The contents of this file are subject to the Mozilla Public License Version |
7 | * 1.1 (the "License"); you may not use this file except in compliance with |
8 | * the License. You may obtain a copy of the License at |
9 | * http://www.mozilla.org/MPL/ |
10 | * |
11 | * Software distributed under the License is distributed on an "AS IS" basis, |
12 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License |
13 | * for the specific language governing rights and limitations under the |
14 | * License. |
15 | * |
16 | * The Original Code is [Open Source Virtual Machine.]. |
17 | * |
18 | * The Initial Developer of the Original Code is |
19 | * Adobe System Incorporated. |
20 | * Portions created by the Initial Developer are Copyright (C) 1993-2006 |
21 | * the Initial Developer. All Rights Reserved. |
22 | * |
23 | * Contributor(s): |
24 | * Adobe AS3 Team |
25 | * |
26 | * Alternatively, the contents of this file may be used under the terms of |
27 | * either the GNU General Public License Version 2 or later (the "GPL"), or |
28 | * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), |
29 | * in which case the provisions of the GPL or the LGPL are applicable instead |
30 | * of those above. If you wish to allow use of your version of this file only |
31 | * under the terms of either the GPL or the LGPL, and not to allow others to |
32 | * use your version of this file under the terms of the MPL, indicate your |
33 | * decision by deleting the provisions above and replace them with the notice |
34 | * and other provisions required by the GPL or the LGPL. If you do not delete |
35 | * the provisions above, a recipient may use your version of this file under |
36 | * the terms of any one of the MPL, the GPL or the LGPL. |
37 | * |
38 | * ***** END LICENSE BLOCK ***** */ |
39 | |
40 | |
41 | #include "avmplus.h" |
42 | |
43 | namespace avmplus |
44 | { |
45 | using namespace MMgc; |
46 | |
47 | // ------------------------------------------------------------------- |
48 | // ------------------------------------------------------------------- |
49 | // ------------------------------------------------------------------- |
50 | // ------------------------------------------------------------------- |
51 | |
52 | /*static*/ TraitsBindings* TraitsBindings::alloc(GC* gc, |
53 | Traits* _owner, |
54 | TraitsBindingsp _base, |
55 | MultinameBindingHashtable* _bindings, |
56 | uint32_t slotCount, |
57 | uint32_t methodCount, |
58 | boolbool typesValid) |
59 | { |
60 | const uint32_t extra = typesValid ? |
61 | slotCount * sizeof(SlotInfo) + methodCount * sizeof(MethodInfo) : |
62 | 0; |
63 | |
64 | TraitsBindings* tb = new (gc, MMgc::kExact, extra) TraitsBindings(_owner, _base, _bindings, slotCount, methodCount, typesValid); |
65 | if (_base && typesValid) |
66 | { |
67 | if (_base->slotCount) |
68 | { |
69 | const SlotInfo* src = &_base->getSlots()[0]; |
70 | SlotInfo* dst = &tb->getSlots()[0]; |
71 | VMPI_memcpy::memcpy(dst, src, _base->slotCount * sizeof(SlotInfo)); |
72 | AvmAssert(((_owner->isMachineType()) || (tb->owner->m_sizeofInstance >= _base->owner->m_sizeofInstance)))do { } while (0); |
73 | } |
74 | if (_base->methodCount) |
75 | VMPI_memcpy::memcpy(&tb->getMethods()[0], &_base->getMethods()[0], _base->methodCount * sizeof(MethodInfo)); |
76 | } |
77 | return tb; |
78 | } |
79 | |
80 | void TraitsBindings::gcTraceHook_TraitsBindings(MMgc::GC* gc) |
81 | { |
82 | if (m_typesValid) |
83 | { |
84 | SlotInfo* slots = getSlots(); |
85 | for ( uint32_t i=0 ; i < slotCount ; i++ ) |
86 | gc->TraceLocation(&slots[i].type); |
87 | TraitsBindings::BindingMethodInfo* methods = getMethods(); |
88 | for ( uint32_t i=0 ; i < methodCount ; i++ ) |
89 | gc->TraceLocation(&methods[i].f); |
90 | } |
91 | } |
92 | |
93 | Binding TraitsBindings::findBinding(Stringp name) const |
94 | { |
95 | for (TraitsBindingsp self = this; self; self = self->base) |
96 | { |
97 | const Binding b = self->m_bindings->getName(name); |
98 | if (b != BIND_NONE) |
99 | return b; |
100 | } |
101 | return BIND_NONE; |
102 | } |
103 | |
104 | Binding TraitsBindings::findBinding(Stringp name, Namespacep ns) const |
105 | { |
106 | for (TraitsBindingsp self = this; self; self = self->base) |
107 | { |
108 | const Binding b = self->m_bindings->get(name, ns); |
109 | if (b != BIND_NONE) |
110 | return b; |
111 | } |
112 | return BIND_NONE; |
113 | } |
114 | |
115 | Binding TraitsBindings::findBinding(Stringp name, NamespaceSetp nsset) const |
116 | { |
117 | for (TraitsBindingsp self = this; self; self = self->base) |
118 | { |
119 | const Binding b = self->m_bindings->get(name, nsset); |
120 | if (b != BIND_NONE) |
121 | return b; |
122 | } |
123 | return BIND_NONE; |
124 | } |
125 | |
126 | Binding TraitsBindings::findBindingAndDeclarer(const Multiname& mn, Traitsp& declarer) const |
127 | { |
128 | if (mn.isBinding()) |
129 | { |
130 | for (TraitsBindingsp self = this; self; self = self->base) |
131 | { |
132 | Namespacep foundns = NULL__null; |
133 | Binding const b = self->m_bindings->getMulti(mn, foundns); |
134 | if (b != BIND_NONE) |
135 | { |
136 | declarer = self->owner; |
137 | |
138 | // if the member is 'protected' then we have to do extra work, |
139 | // as we may have found it in a descendant's protected namespace -- |
140 | // we have to bounce up the inheritance chain and check our parent's |
141 | // protected namespace. |
142 | while (foundns == declarer->protectedNamespace) |
143 | { |
144 | Traitsp declParent = declarer->base; |
145 | if (!declParent || declParent->protectedNamespace == NULL__null) |
146 | break; |
147 | |
148 | Binding const bp = declParent->getTraitsBindings()->findBinding(mn.getName(), declParent->protectedNamespace); |
149 | if (bp != b) |
150 | break; |
151 | |
152 | // self->owner->core->console<<"bounce "<<declarer<<" to "<<declParent<<"\n"; |
153 | declarer = declParent; |
154 | foundns = declParent->protectedNamespace; |
155 | } |
156 | |
157 | // self->owner->core->console<<"final declarer is "<<declarer<<"\n"; |
158 | return b; |
159 | } |
160 | } |
161 | } |
162 | declarer = NULL__null; |
163 | return BIND_NONE; |
164 | } |
165 | |
166 | void TraitsBindings::buildSlotDestroyInfo(MMgc::GC* gc, FixedBitSet& slotDestroyInfo, uint32_t slotAreaCount, uint32_t slotAreaSize) const |
167 | { |
168 | MMGC_STATIC_ASSERT(kUnusedAtomTag == 0 && kObjectType == 1 && kStringType == 2 && kNamespaceType == 3)typedef ::MMgc::static_assert_MMgc<sizeof (::MMgc::STATIC_ASSERTION_FAILED <(bool)(kUnusedAtomTag == 0 && kObjectType == 1 && kStringType == 2 && kNamespaceType == 3)>)> MMgc_static_assert_line_168; |
169 | AvmAssert(slotAreaCount <= slotCount)do { } while (0); |
170 | |
171 | // not the same as slotCount since a slot of type double |
172 | // takes two bits (in 32-bit builds). note that the bits are |
173 | // always 4-byte chunks even in 64-bit builds! |
174 | const uint32_t bitsNeeded = slotAreaSize / sizeof(uint32_t); // not sizeof(Atom)! |
175 | AvmAssert(bitsNeeded * sizeof(uint32_t) == slotAreaSize)do { } while (0); // should be even multiple! |
176 | // allocate one extra bit and use it for "all-zero" |
177 | slotDestroyInfo.resize(gc, bitsNeeded+1); |
178 | if (slotAreaSize > 0) |
179 | { |
180 | const uint32_t sizeofInstance = this->owner->getSizeOfInstance(); |
181 | const TraitsBindings::SlotInfo* tbs = getSlots() + (slotCount - slotAreaCount); |
182 | const TraitsBindings::SlotInfo* tbs_end = tbs + slotAreaCount; |
183 | for ( ; tbs < tbs_end; ++tbs) |
184 | { |
185 | // offset is pointed off the end of our object |
186 | if (isAtomOrRCObjectSlot(tbs->sst())) |
187 | { |
188 | //owner->core->console<<"SDI "<<owner<<" "<<sizeofInstance<<" "<<tbs->type<<" "<<tbs->offset()<<"\n"; |
189 | AvmAssert(tbs->offset() >= sizeofInstance)do { } while (0); |
190 | const uint32_t off = tbs->offset() - sizeofInstance; |
191 | AvmAssert((off % 4) == 0)do { } while (0); |
192 | // if slot is "big" then this is the bit of the first 4 bytes. that's fine. |
193 | slotDestroyInfo.set((off>>2)+1); // +1 to leave room for bit 0 |
194 | slotDestroyInfo.set(0); // bit 0 is "anyset" flag |
195 | } |
196 | // otherwise leave the bit zero |
197 | } |
198 | } |
199 | else |
200 | { |
201 | AvmAssert(slotAreaCount == 0)do { } while (0); |
202 | AvmAssert(bitsNeeded == 0)do { } while (0); |
203 | } |
204 | |
205 | // if nothing set, blow away what we built and realloc as single clear bit -- smaller and faster |
206 | if (!slotDestroyInfo.test(0)) |
207 | { |
208 | slotDestroyInfo.resize(gc, 1); |
209 | AvmAssert(!slotDestroyInfo.test(0))do { } while (0); |
210 | } |
211 | } |
212 | |
213 | boolbool TraitsBindings::checkOverride(AvmCore* core, MethodInfo* virt, MethodInfo* over) const |
214 | { |
215 | if (over == virt) |
216 | return truetrue; |
217 | |
218 | MethodSignaturep overms = over->getMethodSignature(); |
219 | MethodSignaturep virtms = virt->getMethodSignature(); |
220 | |
221 | Traits* overTraits = overms->returnTraits(); |
222 | Traits* virtTraits = virtms->returnTraits(); |
223 | |
224 | if (overTraits != virtTraits) |
225 | { |
226 | #ifdef AVMPLUS_VERBOSE |
227 | core->console << "\n"; |
228 | core->console << "return types dont match\n"; |
229 | core->console << " virt " << virtTraits << " " << virt << "\n"; |
230 | core->console << " over " << overTraits << " " << over << "\n"; |
231 | #endif |
232 | return falsefalse; |
233 | } |
234 | |
235 | if (overms->param_count() != virtms->param_count() || |
236 | overms->optional_count() != virtms->optional_count()) |
237 | { |
238 | #ifdef AVMPLUS_VERBOSE |
239 | core->console << "\n"; |
240 | core->console << "param count mismatch\n"; |
241 | core->console << " virt params=" << virtms->param_count() << " optional=" << virtms->optional_count() << " " << virt << "\n"; |
242 | core->console << " over params=" << overms->param_count() << " optional=" << overms->optional_count() << " " << virt << "\n"; |
243 | #endif |
244 | return falsefalse; |
245 | } |
246 | |
247 | // allow subclass param 0 to implement or extend base param 0 |
248 | virtTraits = virtms->paramTraits(0); |
249 | if (!owner->subtypeof(virtTraits) || !Traits::isMachineCompatible(this->owner, virtTraits)) |
250 | { |
251 | if (!this->owner->isMachineType() && virtTraits == core->traits.object_itraits) |
252 | { |
253 | over->setUnboxThis(); |
254 | } |
255 | else |
256 | { |
257 | #ifdef AVMPLUS_VERBOSE |
258 | core->console << "\n"; |
259 | core->console << "param 0 incompatible\n"; |
260 | core->console << " virt " << virtTraits << " " << virt << "\n"; |
261 | core->console << " over " << this->owner << " " << over << "\n"; |
262 | #endif |
263 | return falsefalse; |
264 | } |
265 | } |
266 | |
267 | for (int k=1, p=overms->param_count(); k <= p; k++) |
268 | { |
269 | overTraits = overms->paramTraits(k); |
270 | virtTraits = virtms->paramTraits(k); |
271 | if (overTraits != virtTraits) |
272 | { |
273 | #ifdef AVMPLUS_VERBOSE |
274 | core->console << "\n"; |
275 | core->console << "param " << k << " incompatible\n"; |
276 | core->console << " virt " << virtTraits << " " << virt << "\n"; |
277 | core->console << " over " << overTraits << " " << over << "\n"; |
278 | #endif |
279 | return falsefalse; |
280 | } |
281 | } |
282 | |
283 | if (virt->unboxThis()) |
284 | { |
285 | // the _unboxThis flag is sticky, all the way down the inheritance tree |
286 | over->setUnboxThis(); |
287 | } |
288 | |
289 | return truetrue; |
290 | } |
291 | |
292 | static boolbool isCompatibleOverrideKind(BindingKind baseKind, BindingKind overKind) |
293 | { |
294 | static const uint8_t kCompatibleBindingKinds[8] = |
295 | { |
296 | 0, // BKIND_NONE |
297 | (1<<BKIND_METHOD), // BKIND_METHOD |
298 | 0, // BKIND_VAR |
299 | 0, // BKIND_CONST |
300 | 0, // unused |
301 | (1<<BKIND_GET) | (1<<BKIND_SET) | (1<<BKIND_GETSET), // BKIND_GET |
302 | (1<<BKIND_GET) | (1<<BKIND_SET) | (1<<BKIND_GETSET), // BKIND_SET |
303 | (1<<BKIND_GET) | (1<<BKIND_SET) | (1<<BKIND_GETSET) // BKIND_GETSET |
304 | }; |
305 | return (kCompatibleBindingKinds[baseKind] & (1<<overKind)) != 0; |
306 | } |
307 | |
308 | boolbool TraitsBindings::checkLegalInterfaces(AvmCore* core) const |
309 | { |
310 | // make sure every interface method is implemented |
311 | for (InterfaceIterator ifc_iter(this); ifc_iter.hasNext();) |
312 | { |
313 | Traits* ifc = ifc_iter.next(); |
314 | |
315 | // don't need to bother checking interfaces in our parent. |
316 | if (this->base && this->base->owner->subtypeof(ifc)) |
317 | continue; |
318 | |
319 | TraitsBindingsp ifcd = ifc->getTraitsBindings(); |
320 | StTraitsBindingsIterator iter(ifcd); |
321 | while (iter.next()) |
322 | { |
323 | Stringp name = iter.key(); |
324 | if (!name) continue; |
325 | Namespacep ns = iter.ns(); |
326 | Binding iBinding = iter.value(); |
327 | const BindingKind iBindingKind = AvmCore::bindingKind(iBinding); |
328 | |
329 | Binding cBinding = this->findBinding(name, ns); |
330 | if (!isCompatibleOverrideKind(iBindingKind, AvmCore::bindingKind(cBinding))) |
331 | { |
332 | // Try again with public namespace that matches the version of the current traits |
333 | const Binding pBinding = this->findBinding(name, core->getPublicNamespace(owner->pool)); |
334 | if (isCompatibleOverrideKind(iBindingKind, AvmCore::bindingKind(pBinding))) |
335 | cBinding = pBinding; |
336 | } |
337 | |
338 | if (iBinding == cBinding) |
339 | continue; |
340 | |
341 | if (!isCompatibleOverrideKind(iBindingKind, AvmCore::bindingKind(cBinding))) |
342 | return falsefalse; |
343 | |
344 | switch (iBindingKind) |
345 | { |
346 | default: |
347 | { |
348 | AvmAssert(0)do { } while (0); // interfaces shouldn't have anything but methods, getters, and setters |
349 | return falsefalse; |
350 | } |
351 | case BKIND_METHOD: |
352 | { |
353 | MethodInfo* virt = ifcd->getMethods()[AvmCore::bindingToMethodId(iBinding)].f; |
354 | MethodInfo* over = this->getMethods()[AvmCore::bindingToMethodId(cBinding)].f; |
355 | if (!checkOverride(core, virt, over)) |
356 | return falsefalse; |
357 | break; |
358 | } |
359 | case BKIND_GET: |
360 | case BKIND_SET: |
361 | case BKIND_GETSET: |
362 | { |
363 | // check getter & setter overrides |
364 | if (AvmCore::hasGetterBinding(iBinding)) |
365 | { |
366 | if (!AvmCore::hasGetterBinding(cBinding)) |
367 | return falsefalse; |
368 | |
369 | MethodInfo* virt = ifcd->getMethods()[AvmCore::bindingToGetterId(iBinding)].f; |
370 | MethodInfo* over = this->getMethods()[AvmCore::bindingToGetterId(cBinding)].f; |
371 | if (!checkOverride(core, virt, over)) |
372 | return falsefalse; |
373 | } |
374 | |
375 | if (AvmCore::hasSetterBinding(iBinding)) |
376 | { |
377 | if (!AvmCore::hasSetterBinding(cBinding)) |
378 | return falsefalse; |
379 | |
380 | MethodInfo* virt = ifcd->getMethod(AvmCore::bindingToSetterId(iBinding)); |
381 | MethodInfo* over = this->getMethod(AvmCore::bindingToSetterId(cBinding)); |
382 | if (!checkOverride(core, virt, over)) |
383 | return falsefalse; |
384 | } |
385 | } |
386 | break; |
387 | } // switch |
388 | } // for j |
389 | } // for tbi |
390 | return truetrue; |
391 | } |
392 | |
393 | void TraitsBindings::fixOneInterfaceBindings(Traitsp ifc) |
394 | { |
395 | AvmAssert(!owner->isInterface())do { } while (0); |
396 | |
397 | TraitsBindingsp ifcd = ifc->getTraitsBindings(); |
398 | StTraitsBindingsIterator iter(ifcd); |
399 | while (iter.next()) |
400 | { |
401 | Stringp name = iter.key(); |
402 | if (!name) continue; |
403 | Namespacep ns = iter.ns(); |
404 | Binding iBinding = iter.value(); |
405 | const BindingKind iBindingKind = AvmCore::bindingKind(iBinding); |
406 | const Binding cBinding = this->findBinding(name, ns); |
407 | if (!isCompatibleOverrideKind(iBindingKind, AvmCore::bindingKind(cBinding))) |
408 | { |
409 | // Try again with public namespace that matches the version of the current traits |
410 | const Binding pBinding = this->findBinding(name, ifc->core->getPublicNamespace(owner->pool)); |
411 | if (isCompatibleOverrideKind(iBindingKind, AvmCore::bindingKind(pBinding))) |
412 | { |
413 | this->m_bindings->add(name, ns, pBinding); |
414 | } |
415 | } |
416 | } |
417 | } |
418 | |
419 | void Traits::addVersionedBindings(MultinameBindingHashtable* bindings, |
420 | Stringp name, |
421 | NamespaceSetp nss, |
422 | Binding binding) const |
423 | { |
424 | // find the lowest version within the active series. |
425 | ApiVersion apiVersion = kApiVersion_VM_INTERNAL; |
426 | for (NamespaceSetIterator iter(nss); iter.hasNext();) |
427 | { |
428 | Namespacep ns = iter.next(); |
429 | ApiVersion a = core->getValidApiVersion(ns->getApiVersion()); |
430 | if (a < apiVersion) |
431 | apiVersion = a; |
432 | } |
433 | Namespacep ns = core->getVersionedNamespace(nss->nsAt(0), apiVersion); |
434 | bindings->add(name, ns, binding); |
435 | } |
436 | |
437 | // ------------------------------------------------------------------- |
438 | // ------------------------------------------------------------------- |
439 | |
440 | TraitsMetadata::MetadataPtr TraitsMetadata::getSlotMetadataPos(uint32_t i, PoolObject*& residingPool) const |
441 | { |
442 | AvmAssert(i < slotCount)do { } while (0); |
443 | residingPool = NULL__null; |
444 | for (TraitsMetadatap self = this; self && (i < self->slotCount); self = self->base) |
445 | { |
446 | MetadataPtr pos = self->slotMetadataPos[i]; |
447 | if (pos) |
448 | { |
449 | residingPool = self->residingPool; |
450 | return pos; |
451 | } |
452 | } |
453 | return NULL__null; |
454 | } |
455 | |
456 | TraitsMetadata::MetadataPtr TraitsMetadata::getMethodMetadataPos(uint32_t i, PoolObject*& residingPool) const |
457 | { |
458 | AvmAssert(i < methodCount)do { } while (0); |
459 | residingPool = NULL__null; |
460 | for (TraitsMetadatap self = this; self && (i < self->methodCount); self = self->base) |
461 | { |
462 | MetadataPtr pos = self->methodMetadataPos[i]; |
463 | if (pos) |
464 | { |
465 | residingPool = self->residingPool; |
466 | return pos; |
467 | } |
468 | } |
469 | return NULL__null; |
470 | } |
471 | |
472 | // ------------------------------------------------------------------- |
473 | // ------------------------------------------------------------------- |
474 | |
475 | Traits::Traits(PoolObject* _pool, |
476 | Traits* _base, |
477 | uint16_t _sizeofInstance, |
478 | uint16_t _offsetofSlots, |
479 | TraitsPosPtr traitsPos, |
480 | TraitsPosType posType) |
481 | : core(_pool->core) |
482 | , base(_base) |
483 | , pool(_pool) |
484 | , m_traitsPos(traitsPos) |
485 | , m_tbref(_pool->core->GetGC()->emptyWeakRef) |
486 | , m_tmref(_pool->core->GetGC()->emptyWeakRef) |
487 | , m_sizeofInstance(_sizeofInstance) |
488 | , m_offsetofSlots(_offsetofSlots) |
489 | , builtinType(BUILTIN_none) |
490 | , m_posType(uint8_t(posType)) |
491 | // Assume builtins initialize this correctly. This is verified |
492 | // by checkCustomConstruct() in MethodEnv.cpp. |
493 | , hasCustomConstruct(0) |
494 | #ifdef VMCFG_AOT |
495 | , m_interfaceBindingFunction(NULL__null) |
496 | #endif |
497 | { |
498 | AvmAssert(m_tbref->isNull())do { } while (0); |
499 | AvmAssert(m_tmref->isNull())do { } while (0); |
500 | AvmAssert(BUILTIN_COUNT <= 32)do { } while (0); |
501 | AvmAssert(m_slotDestroyInfo.allocatedSize() == 0)do { } while (0); |
502 | #ifdef _DEBUG |
503 | switch (posType) |
504 | { |
505 | case TRAITSTYPE_NVA: |
506 | case TRAITSTYPE_RT: |
507 | AvmAssert(m_traitsPos == 0)do { } while (0); |
508 | break; |
509 | default: |
510 | AvmAssert(m_traitsPos != 0)do { } while (0); |
511 | break; |
512 | } |
513 | #endif |
514 | build_primary_supertypes(); |
515 | build_secondary_supertypes(); |
516 | } |
517 | |
518 | /*static*/ Traits* Traits::newTraits(PoolObject* pool, |
519 | Traits *base, |
520 | uint16_t objectSize, |
521 | uint16_t offsetOfSlots, |
522 | TraitsPosPtr traitsPos, |
523 | TraitsPosType posType) |
524 | { |
525 | AvmAssert(posType != TRAITSTYPE_CATCH)do { } while (0); |
526 | AvmAssert(pool != NULL)do { } while (0); |
527 | Traits* traits = new (pool->core->GetGC(), MMgc::kExact) Traits(pool, base, objectSize, offsetOfSlots, traitsPos, posType); |
528 | return traits; |
529 | } |
530 | |
531 | /*static*/ Traits* Traits::newCatchTraits(const Toplevel* toplevel, PoolObject* pool, TraitsPosPtr traitsPos, Stringp name, Namespacep ns) |
532 | { |
533 | AvmAssert(pool != NULL)do { } while (0); |
534 | Traits* traits = new (pool->core->GetGC(), MMgc::kExact) Traits(pool, NULL__null, sizeof(ScriptObject), sizeof(ScriptObject), traitsPos, TRAITSTYPE_CATCH); |
535 | traits->final = truetrue; |
536 | traits->set_names(ns, name); |
537 | traits->verifyBindings(toplevel); |
538 | traits->resolveSignatures(toplevel); |
539 | return traits; |
540 | } |
541 | |
542 | Traits* Traits::_newParameterizedTraits(Stringp name, Namespacep ns, Traits* _base) |
543 | { |
544 | Traits* newtraits = Traits::newTraits(this->pool, _base, this->getSizeOfInstance(), this->m_offsetofSlots, NULL__null, TRAITSTYPE_RT); |
545 | newtraits->m_needsHashtable = this->m_needsHashtable; |
546 | newtraits->set_names(ns, name); |
547 | return newtraits; |
548 | } |
549 | |
550 | TraitsPosPtr Traits::traitsPosStart() const |
551 | { |
552 | TraitsPosPtr pos = m_traitsPos; |
553 | switch (posType()) |
554 | { |
555 | case TRAITSTYPE_INTERFACE: |
556 | case TRAITSTYPE_INSTANCE: |
557 | pos = skipToInstanceInitPos(pos); |
558 | // fall thru, no break |
559 | |
560 | case TRAITSTYPE_CLASS: |
561 | case TRAITSTYPE_SCRIPT: |
562 | AvmCore::skipU32(pos, 1); // skip in init_index |
563 | break; |
564 | |
565 | case TRAITSTYPE_ACTIVATION: |
566 | // nothing to do |
567 | break; |
568 | |
569 | case TRAITSTYPE_CATCH: |
570 | case TRAITSTYPE_NVA: |
571 | case TRAITSTYPE_RT: |
572 | pos = NULL__null; |
573 | break; |
574 | } |
575 | return pos; |
576 | } |
577 | |
578 | TraitsPosPtr Traits::skipToInstanceInitPos(TraitsPosPtr pos) const |
579 | { |
580 | AvmAssert(isInstanceType() && pos != NULL)do { } while (0); |
581 | AvmCore::skipU32(pos, 2); // skip the QName & base traits |
582 | const uint8_t theflags = *pos++; |
583 | const boolbool hasProtected = (theflags & 8) != 0; |
584 | if (hasProtected) |
585 | { |
586 | AvmCore::skipU32(pos); |
587 | } |
588 | const uint32_t interfaceCount = AvmCore::readU32(pos); |
589 | AvmCore::skipU32(pos, interfaceCount); |
590 | return pos; |
591 | } |
592 | |
593 | static boolbool is8ByteSlot(Traits* slotTE) |
594 | { |
595 | #ifdef AVMPLUS_64BIT |
596 | const uint32_t BIG_TYPE_MASK = ~((1U<<BUILTIN_int) | (1U<<BUILTIN_uint) | (1U<<BUILTIN_boolean)); |
597 | #else |
598 | const uint32_t BIG_TYPE_MASK = (1U<<BUILTIN_number); |
599 | #endif |
600 | |
601 | return ((1 << Traits::getBuiltinType(slotTE)) & BIG_TYPE_MASK) != 0; |
602 | } |
603 | |
604 | // Sun compilers don't allow static and REALLY_INLINE |
605 | /*static*/ REALLY_INLINEinline __attribute__((always_inline)) int32_t pad8(int32_t nextSlotOffset) |
606 | { |
607 | // 8-aligned, 8-byte field |
608 | if (nextSlotOffset & 7) |
609 | { |
610 | AvmAssert((nextSlotOffset % 4) == 0)do { } while (0); // should always be a multiple of 4 |
611 | nextSlotOffset += 4; |
612 | } |
613 | int32_t slotOffset = nextSlotOffset; |
614 | nextSlotOffset += 8; |
Value stored to 'nextSlotOffset' is never read | |
615 | return slotOffset; |
616 | } |
617 | |
618 | SlotStorageType valueStorageType(BuiltinType bt) |
619 | { |
620 | switch (bt) |
621 | { |
622 | case BUILTIN_int: return SST_int32; |
623 | case BUILTIN_uint: return SST_uint32; |
624 | case BUILTIN_number: return SST_double; |
625 | case BUILTIN_boolean: return SST_bool32; |
626 | case BUILTIN_void: return SST_atom; |
627 | case BUILTIN_any: return SST_atom; |
628 | case BUILTIN_object: return SST_atom; |
629 | case BUILTIN_string: return SST_string; |
630 | case BUILTIN_namespace: return SST_namespace; |
631 | default: return SST_scriptobject; |
632 | } |
633 | } |
634 | |
635 | REALLY_INLINEinline __attribute__((always_inline)) SlotStorageType slotStorageType(BuiltinType bt) |
636 | { |
637 | AvmAssert(bt != BUILTIN_void)do { } while (0); // actual slots cannot be type void. |
638 | return valueStorageType(bt); |
639 | } |
640 | |
641 | // Sun compilers don't allow static and REALLY_INLINE |
642 | /*static*/ REALLY_INLINEinline __attribute__((always_inline)) boolbool isPointerSlot(Traits* slotTE) |
643 | { |
644 | BuiltinType bt = Traits::getBuiltinType(slotTE); |
645 | AvmAssert(bt != BUILTIN_void)do { } while (0); |
646 | |
647 | MMGC_STATIC_ASSERT(BUILTIN_COUNT < (sizeof(int) * 8))typedef ::MMgc::static_assert_MMgc<sizeof (::MMgc::STATIC_ASSERTION_FAILED <(bool)(BUILTIN_COUNT < (sizeof(int) * 8))>)> MMgc_static_assert_line_647; |
648 | int const IS_POINTER = ~((1<<BUILTIN_int)|(1<<BUILTIN_uint)|(1<<BUILTIN_number)|(1<<BUILTIN_boolean)); |
649 | |
650 | return ((1 << bt) & IS_POINTER) != 0; |
651 | } |
652 | |
653 | // the logic for assigning slot id's is used in several places, so it's now collapsed here |
654 | // rather than redundantly sprinkled thru several bits of code. |
655 | class SlotIdCalcer |
656 | { |
657 | private: |
658 | uint32_t m_slotCount; |
659 | boolbool m_earlySlotBinding; |
660 | public: |
661 | SlotIdCalcer(uint32_t _baseSlotCount, boolbool _earlySlotBinding) : |
662 | m_slotCount(_baseSlotCount), |
663 | m_earlySlotBinding(_earlySlotBinding) |
664 | { |
665 | } |
666 | |
667 | uint32_t calc_id(uint32_t id) |
668 | { |
669 | if (!id || !m_earlySlotBinding) |
670 | { |
671 | id = ++m_slotCount; |
672 | } |
673 | if (m_slotCount < id) |
674 | m_slotCount = id; |
675 | return id - 1; |
676 | } |
677 | |
678 | uint32_t slotCount() const { return m_slotCount; } |
679 | boolbool earlySlotBinding() const { return m_earlySlotBinding; } |
680 | }; |
681 | |
682 | struct NameEntry |
683 | { |
684 | const uint8_t* meta_pos; |
685 | uint32_t qni, id, info, value_index; |
686 | CPoolKind value_kind; |
687 | TraitKind kind; |
688 | uint8_t tag; |
689 | |
690 | void readNameEntry(const uint8_t*& pos); |
691 | }; |
692 | |
693 | void NameEntry::readNameEntry(const uint8_t*& pos) |
694 | { |
695 | qni = AvmCore::readU32(pos); |
696 | tag = *pos++; |
697 | kind = (TraitKind) (tag & 0x0f); |
698 | value_kind = CONSTANT_unused_0x00; |
699 | value_index = 0; |
700 | |
701 | // Read in the trait entry. |
702 | switch (kind) |
703 | { |
704 | case TRAIT_Slot: |
705 | case TRAIT_Const: |
706 | id = AvmCore::readU32(pos); // slot id |
707 | info = AvmCore::readU32(pos); // value type |
708 | value_index = AvmCore::readU32(pos); // value index |
709 | if (value_index) |
710 | value_kind = (CPoolKind)*pos++; // value kind |
711 | break; |
712 | case TRAIT_Class: |
713 | id = AvmCore::readU32(pos); // slot id |
714 | info = AvmCore::readU32(pos); // classinfo |
715 | break; |
716 | case TRAIT_Getter: |
717 | case TRAIT_Setter: |
718 | case TRAIT_Method: |
719 | AvmCore::skipU32(pos); // disp id (never used) |
720 | id = AvmCore::readU32(pos); // method index |
721 | info = 0; |
722 | break; |
723 | default: |
724 | // unsupported traits type -- can't happen, caught in AbcParser::parseTraits |
725 | AvmAssert(0)do { } while (0); |
726 | break; |
727 | } |
728 | meta_pos = pos; |
729 | if (tag & ATTR_metadata) |
730 | { |
731 | uint32_t metaCount = AvmCore::readU32(pos); |
732 | AvmCore::skipU32(pos, metaCount); |
733 | } |
734 | } |
735 | |
736 | boolbool Traits::allowEarlyBinding() const |
737 | { |
738 | // the compiler can early bind to a type's slots when it's defined |
739 | // or when the base class came from another abc file and has zero slots |
740 | // this ensures you cant use the early opcodes to access an external type's |
741 | // private members. |
742 | TraitsBindingsp tb = this->base ? this->base->getTraitsBindings() : NULL__null; |
743 | while (tb != NULL__null && tb->slotCount > 0) |
744 | { |
745 | if (tb->owner->pool != this->pool && tb->slotCount > 0) |
746 | { |
747 | return falsefalse; |
748 | } |
749 | tb = tb->base; |
750 | } |
751 | return truetrue; |
752 | } |
753 | |
754 | void Traits::buildBindings(TraitsBindingsp basetb, |
755 | MultinameBindingHashtable* bindings, |
756 | uint32_t& slotCount, |
757 | uint32_t& methodCount, |
758 | SlotSizeInfo* slotSizeInfo, |
759 | const Toplevel* toplevel) const |
760 | { |
761 | const uint8_t* pos = traitsPosStart(); |
762 | |
763 | const uint32_t baseSlotCount = basetb ? basetb->slotCount : 0; |
764 | const uint32_t baseMethodCount = basetb ? basetb->methodCount : 0; |
765 | |
766 | //slotCount = baseSlotCount; |
767 | methodCount = baseMethodCount; |
768 | |
769 | SlotIdCalcer sic(baseSlotCount, this->allowEarlyBinding()); |
770 | |
771 | #ifdef _DEBUG |
772 | uint32_t pointerSlots = 0; |
773 | #endif |
774 | |
775 | NameEntry ne; |
776 | const uint32_t nameCount = pos ? AvmCore::readU32(pos) : 0; |
777 | for (uint32_t i = 0; i < nameCount; i++) |
778 | { |
779 | ne.readNameEntry(pos); |
780 | Multiname mn; |
781 | this->pool->resolveBindingNameNoCheck(ne.qni, mn, toplevel); |
782 | Stringp name = mn.getName(); |
783 | Namespacep ns; |
784 | NamespaceSetp compat_nss; |
785 | if (mn.namespaceCount() > 1) { |
786 | ns = mn.getNsset()->nsAt(0); |
787 | compat_nss = mn.getNsset(); |
788 | } |
789 | else { |
790 | ns = mn.getNamespace(); |
791 | compat_nss = NamespaceSet::create(core->GetGC(), ns); |
792 | } |
793 | |
794 | switch (ne.kind) |
795 | { |
796 | case TRAIT_Slot: |
797 | case TRAIT_Const: |
798 | case TRAIT_Class: |
799 | { |
800 | uint32_t slot_id = sic.calc_id(ne.id); |
801 | if (toplevel) |
802 | { |
803 | AvmAssert(!this->m_resolved)do { } while (0); |
804 | |
805 | // when we resolve, we must do some additional verification checks... these were formerly |
806 | // done in AbcParser::parseTraits but require the base class to be resolved first, so we |
807 | // now defer it to here. |
808 | |
809 | // illegal raw slot id -- reject IF we are early binding. (theoretically it's always illegal, |
810 | // but won't cause any problems if earlySlotBinding=false, and rejecting in this case |
811 | // will break existing content.) |
812 | if (ne.id > nameCount && sic.earlySlotBinding()) |
813 | toplevel->throwVerifyError(kCorruptABCError); |
814 | |
815 | // slots are final. |
816 | if (basetb && slot_id < basetb->slotCount) |
817 | toplevel->throwVerifyError(kIllegalOverrideError, core->toErrorString(mn), core->toErrorString(base)); |
818 | |
819 | // a slot cannot override anything else. |
820 | if (bindings->get(name, ns) != BIND_NONE) |
821 | toplevel->throwVerifyError(kCorruptABCError); |
822 | |
823 | // In theory we should reject duplicate slots here; |
824 | // in practice we don't, as it causes problems with some existing content |
825 | //if (basetb->findBinding(name, ns) != BIND_NONE) |
826 | // toplevel->throwVerifyError(kIllegalOverrideError, toplevel->core()->toErrorString(qn), toplevel->core()->toErrorString(this)); |
827 | |
828 | // Interfaces cannot have slots. |
829 | if (this->isInterface()) |
830 | toplevel->throwVerifyError(kIllegalSlotError, core->toErrorString(this)); |
831 | |
832 | } |
833 | // these assertions are commented out because the new lazy-init code can |
834 | // fire them inapppriately -- they all indicate malformed ABC, but they |
835 | // *can* occur on a pre-resolve build of TraitsBindings. We'll just let |
836 | // them slide by since we check for them all at resolve time. |
837 | // AvmAssert(!(ne.id > nameCount)); // unhandled verify error |
838 | // AvmAssert(!(basetb && slot_id < basetb->slotCount)); // unhandled verify error |
839 | // AvmAssert(!(bindings->get(name, ns) != BIND_NONE)); // unhandled verify error |
840 | addVersionedBindings(bindings, name, compat_nss, AvmCore::makeSlotBinding(slot_id, ne.kind==TRAIT_Slot ? BKIND_VAR : BKIND_CONST)); |
841 | if (slotSizeInfo != NULL__null) |
842 | { |
843 | // only do this if we need it -- doing it too early could cause us to reference a not-yet-loaded |
844 | // type which we don't need yet, causing an unnecessary verification failure |
845 | Traitsp slotType = (ne.kind == TRAIT_Class) ? |
846 | pool->getClassTraits(ne.info) : |
847 | pool->resolveTypeName(ne.info, toplevel); |
848 | if (!isPointerSlot(slotType)) |
849 | { |
850 | if (is8ByteSlot(slotType)) |
851 | slotSizeInfo->nonPointer64BitSlotCount += 1; |
852 | else |
853 | slotSizeInfo->nonPointer32BitSlotCount += 1; |
854 | } |
855 | else |
856 | { |
857 | #ifdef _DEBUG |
858 | pointerSlots += 1; |
859 | #endif |
860 | } |
861 | } |
862 | break; |
863 | } |
864 | case TRAIT_Method: |
865 | { |
866 | Binding baseBinding = this->getOverride(basetb, ns, name, ne.tag, toplevel); |
867 | if (baseBinding == BIND_NONE) |
868 | { |
869 | addVersionedBindings(bindings, name, compat_nss, AvmCore::makeMGSBinding(methodCount, BKIND_METHOD)); |
870 | // accessors require 2 vtable slots, methods only need 1. |
871 | methodCount += 1; |
872 | } |
873 | else if (AvmCore::isMethodBinding(baseBinding)) |
874 | { |
875 | // something got overridden, need new name entry for this subclass |
876 | // but keep the existing disp_id |
877 | addVersionedBindings(bindings, name, compat_nss, baseBinding); |
878 | } |
879 | else |
880 | { |
881 | if (toplevel) |
882 | toplevel->throwVerifyError(kCorruptABCError); |
883 | AvmAssert(!"unhandled verify error")do { } while (0); |
884 | } |
885 | break; |
886 | } |
887 | case TRAIT_Getter: |
888 | case TRAIT_Setter: |
889 | { |
890 | // if nothing already is defined in this class, use base class in case getter/setter has already been defined. |
891 | Binding baseBinding = bindings->get(name, ns); |
892 | if (baseBinding == BIND_NONE) |
893 | baseBinding = this->getOverride(basetb, ns, name, ne.tag, toplevel); |
894 | |
895 | const BindingKind us = (ne.kind == TRAIT_Getter) ? BKIND_GET : BKIND_SET; |
896 | const BindingKind them = (ne.kind == TRAIT_Getter) ? BKIND_SET : BKIND_GET; |
897 | if (baseBinding == BIND_NONE) |
898 | { |
899 | addVersionedBindings(bindings, name, compat_nss, AvmCore::makeMGSBinding(methodCount, us)); |
900 | // accessors require 2 vtable slots, methods only need 1. |
901 | methodCount += 2; |
902 | } |
903 | else if (AvmCore::isAccessorBinding(baseBinding)) |
904 | { |
905 | // something maybe got overridden, need new name entry for this subclass |
906 | // but keep the existing disp_id |
907 | // both get & set bindings use the get id. set_id = get_id + 1. |
908 | if (AvmCore::bindingKind(baseBinding) == them) |
909 | { |
910 | baseBinding = AvmCore::makeGetSetBinding(baseBinding); |
911 | } |
912 | addVersionedBindings(bindings, name, compat_nss, baseBinding); |
913 | } |
914 | else |
915 | { |
916 | if (toplevel) |
917 | toplevel->throwVerifyError(kCorruptABCError); |
918 | AvmAssert(!"unhandled verify error")do { } while (0); |
919 | } |
920 | break; |
921 | } |
922 | |
923 | default: |
924 | // unsupported traits type -- can't happen, caught in AbcParser::parseTraits |
925 | AvmAssert(0)do { } while (0); |
926 | break; |
927 | } |
928 | } // for i |
929 | slotCount = sic.slotCount(); |
930 | if (slotSizeInfo) |
931 | { |
932 | uint32_t const c = slotSizeInfo->nonPointer32BitSlotCount + slotSizeInfo->nonPointer64BitSlotCount + baseSlotCount; |
933 | // We calculate this at the end because we might have a sparse slot table... |
934 | // the unused slots will be, well, unused, but we still have to reserve space |
935 | // for them to guard against pathological code. |
936 | slotSizeInfo->pointerSlotCount = slotCount - c; |
937 | // Don't do this assertion: if this situation occurs, we'll catch it (and throw |
938 | // a VerifyError) in finishSlotsAndMethods; asserting here will just spoil acceptance test runs. |
939 | // AvmAssert(slotSizeInfo->pointerSlotCount >= pointerSlots); |
940 | } |
941 | } |
942 | |
943 | namespace |
944 | { |
945 | // Don't mess with the order of the members of the |
946 | // following struct. |
947 | // |
948 | // This struct is never actually instantiated, it is |
949 | // only used to determine the default padding that C++ |
950 | // compiler will insert between 32 bit member variables and |
951 | // 64 bit member variables. |
952 | struct GlueClassTest_Slots |
953 | { |
954 | int32_t m_intSlot; |
955 | double m_numberSlot; |
956 | int32_t m_otherIntSlot; |
957 | void* m_ptrSlot; |
958 | }; |
959 | static const boolbool align8ByteSlots = (offsetof(GlueClassTest_Slots, m_numberSlot)__builtin_offsetof(GlueClassTest_Slots, m_numberSlot) == 8); |
960 | static const boolbool alignPointersTo8Bytes = (offsetof(GlueClassTest_Slots, m_ptrSlot)__builtin_offsetof(GlueClassTest_Slots, m_ptrSlot) == 24); |
961 | static const boolbool is64Bit = sizeof(void*) == 8; |
962 | } |
963 | |
964 | // Sun compilers don't allow static and REALLY_INLINE |
965 | /*static*/ REALLY_INLINEinline __attribute__((always_inline)) int32_t computeSlotOffset(Traits* slotType, int32_t& next32BitSlotOffset, int32_t& nextPointerSlotOffset, int32_t& next64BitSlotOffset) |
966 | { |
967 | if (isPointerSlot(slotType)) |
968 | { |
969 | int32_t const result = nextPointerSlotOffset; |
970 | nextPointerSlotOffset += sizeof(void*); |
971 | return result; |
972 | } |
973 | else if (is8ByteSlot(slotType)) |
974 | { |
975 | int32_t const result = next64BitSlotOffset; |
976 | next64BitSlotOffset += 8; |
977 | return result; |
978 | } |
979 | else |
980 | { |
981 | int32_t const result = next32BitSlotOffset; |
982 | next32BitSlotOffset += 4; |
983 | return result; |
984 | } |
985 | } |
986 | |
987 | void Traits::computeNonNativeSlotAreaCountAndSize(TraitsBindings* tb, uint32_t& slotCount, uint32_t& size) const |
988 | { |
989 | const TraitsBindings* prevBindings = tb; |
990 | const TraitsBindings* currBindings = tb->base; |
991 | uint32_t thisSize = getSizeOfInstance(); |
992 | while ((currBindings != NULL__null) && (currBindings->owner->getSizeOfInstance() == thisSize)) |
993 | { |
994 | const TraitsBindings* baseBindings = currBindings->base; |
995 | AvmAssert((baseBindings == 0) || (baseBindings->owner->getSizeOfInstance() <= thisSize))do { } while (0); (void)baseBindings; |
996 | prevBindings = currBindings; |
997 | currBindings = currBindings->base; |
998 | } |
999 | |
1000 | // currBindings is first ancestor class that is native with at least one slot or native member variable |
1001 | // or null if there is no native ancenstor class |
1002 | |
1003 | if (currBindings == NULL__null) |
1004 | { |
1005 | // We could not find a ancestor class that is native. |
1006 | slotCount = tb->slotCount; |
1007 | size = tb->m_slotSize; |
1008 | } |
1009 | else if (currBindings == tb->base) |
1010 | { |
1011 | AvmAssert(tb->base != NULL)do { } while (0); |
1012 | AvmAssert((tb->base->slotCount == 0) || (tb->base->owner->getSizeOfInstance() <= thisSize))do { } while (0); |
1013 | // "this" is a native class. |
1014 | slotCount = 0; |
1015 | size = 0; |
1016 | } |
1017 | else |
1018 | { |
1019 | AvmAssert(tb->base != NULL)do { } while (0); |
1020 | AvmAssert(currBindings != NULL)do { } while (0); |
1021 | AvmAssert(currBindings->owner->getSizeOfInstance() <= thisSize)do { } while (0); |
1022 | AvmAssert(tb->slotCount >= prevBindings->slotCount)do { } while (0); |
1023 | AvmAssert(tb->m_slotSize >= prevBindings->m_slotSize)do { } while (0); |
1024 | slotCount = tb->slotCount - prevBindings->slotCount; |
1025 | size = tb->m_slotSize - prevBindings->m_slotSize; |
1026 | } |
1027 | } |
1028 | |
1029 | uint32_t Traits::finishSlotsAndMethods(TraitsBindingsp basetb, |
1030 | TraitsBindings* tb, |
1031 | const Toplevel* toplevel, |
1032 | const SlotSizeInfo& sizeInfo) const |
1033 | { |
1034 | const uint8_t* pos = traitsPosStart(); |
1035 | |
1036 | SlotIdCalcer sic(basetb ? basetb->slotCount : 0, this->allowEarlyBinding()); |
1037 | |
1038 | /* |
1039 | A word of caution about calculating slot layout: |
1040 | |
1041 | Say you have two classes like so: |
1042 | |
1043 | class A { ... }; |
1044 | class B : public A { int32 foo; }; |
1045 | |
1046 | You might assume that sizeof(B) > sizeof(A). |
1047 | |
1048 | However... if the contents of A are |
1049 | |
1050 | class A { char foo[9]; }; |
1051 | class B : public A { int32 bar; }; |
1052 | |
1053 | The compiler may decide that sizeof(A) should be rounded |
1054 | up to the nearest pointer-sized boundary (e.g. if it |
1055 | contains virtual methods)... so for a 64-bit build, |
1056 | sizeof(A) could be 16. So sizeof(B) is 20, right? |
1057 | |
1058 | Nope! Not necesssarily. Apparently GCC is clever enough to |
1059 | realize that there is enough unused (and appropriately aligned) |
1060 | space at the end of A to fit the extra fields in B... |
1061 | so sizeof(A) == sizeof(B) == 16. |
1062 | |
1063 | Similarly, we could have |
1064 | |
1065 | class A { char foo[9]; }; |
1066 | class B : public A { int32 bar[3]; }; |
1067 | |
1068 | which could yield sizeof(A) == 16, sizeof(B) == 24, which violates |
1069 | the previously-assumed invariant of (sizeof(B)-sizeof(A)>=slotsize(B)). |
1070 | |
1071 | (Note that this behavior is theoretically possible on 32-bit builds as well, |
1072 | but since the slots only contain 32-bit-or-larger fields, there can't ever be |
1073 | enough properly-aligned padding for this to happen.) |
1074 | |
1075 | Anyway, the point of this digression is to point out that you can't ever assume |
1076 | that our slotArea starts "after" our parent; there might be overlap. m_offsetofSlots |
1077 | is computed for all native classes using C++ offsetof(), and thus correctly |
1078 | accounts for any overlap of this sort. See bugzilla 655300 for more info. |
1079 | |
1080 | */ |
1081 | |
1082 | int32_t slotAreaStart = m_offsetofSlots; |
1083 | if (slotAreaStart == 0 && base != NULL__null) |
1084 | { |
1085 | // 0 means "put the slots at the end of my immediate parent" |
1086 | slotAreaStart = base->getHashtableOffset() ? |
1087 | base->getHashtableOffset() : |
1088 | base->getTotalSize(); |
1089 | } |
1090 | |
1091 | |
1092 | int32_t next32BitSlotOffset = slotAreaStart; |
1093 | int32_t endOf32BitSlots = next32BitSlotOffset + (sizeInfo.nonPointer32BitSlotCount * 4); |
1094 | int32_t nextPointerSlotOffset = alignPointersTo8Bytes && (sizeInfo.pointerSlotCount != 0) ? pad8(endOf32BitSlots) : endOf32BitSlots; |
1095 | int32_t endOfPointerSlots = nextPointerSlotOffset + (sizeInfo.pointerSlotCount * sizeof(void*)); |
1096 | int32_t next64BitSlotOffset = align8ByteSlots && (sizeInfo.nonPointer64BitSlotCount != 0) ? pad8(endOfPointerSlots) : endOfPointerSlots; |
1097 | int32_t endOf64BitSlots = next64BitSlotOffset + (sizeInfo.nonPointer64BitSlotCount * 8); |
1098 | |
1099 | NameEntry ne; |
1100 | const uint32_t nameCount = pos ? AvmCore::readU32(pos) : 0; |
1101 | for (uint32_t i = 0; i < nameCount; i++) |
1102 | { |
1103 | AvmAssert(next32BitSlotOffset <= endOf32BitSlots)do { } while (0); |
1104 | AvmAssert(nextPointerSlotOffset <= endOfPointerSlots)do { } while (0); |
1105 | AvmAssert(next64BitSlotOffset <= endOf64BitSlots)do { } while (0); (void)endOf64BitSlots; |
1106 | ne.readNameEntry(pos); |
1107 | Multiname mn; |
1108 | this->pool->resolveBindingNameNoCheck(ne.qni, mn, toplevel); |
1109 | Namespacep ns = mn.getNamespace(); |
1110 | Stringp name = mn.getName(); |
1111 | // NOTE only one versioned namespace from the set needed here |
1112 | |
1113 | switch (ne.kind) |
1114 | { |
1115 | case TRAIT_Slot: |
1116 | case TRAIT_Const: |
1117 | case TRAIT_Class: |
1118 | { |
1119 | uint32_t slotid = sic.calc_id(ne.id); |
1120 | if (slotid >= tb->slotCount || tb->getSlotOffset(slotid) > 0) |
1121 | { |
1122 | if (toplevel) |
1123 | toplevel->throwVerifyError(kCorruptABCError); |
1124 | AvmAssert(!"unhandled verify error")do { } while (0); |
1125 | } |
1126 | // note, for TRAIT_Class, AbcParser::parseTraits has already verified that pool->cinits[ne.info] is not null |
1127 | Traitsp slotType = (ne.kind == TRAIT_Class) ? |
1128 | pool->getClassTraits(ne.info) : |
1129 | pool->resolveTypeName(ne.info, toplevel); |
1130 | uint32_t slotOffset = computeSlotOffset(slotType, next32BitSlotOffset, nextPointerSlotOffset, next64BitSlotOffset); |
1131 | AvmAssert(slotOffset >= sizeof(ScriptObject))do { } while (0); |
1132 | tb->setSlotInfo(slotid, slotType, slotStorageType(getBuiltinType(slotType)), slotOffset); |
1133 | break; |
1134 | } |
1135 | case TRAIT_Getter: |
1136 | case TRAIT_Setter: |
1137 | case TRAIT_Method: |
1138 | { |
1139 | const Binding b = tb->m_bindings->get(name, ns); |
1140 | if (b != BIND_NONE) |
1141 | { |
1142 | const uint32_t disp_id = uint32_t(uintptr_t(b) >> 3) + (ne.kind == TRAIT_Setter); |
1143 | Binding base = this->getOverride(basetb, ns, name, ne.tag, toplevel); |
1144 | if (AvmCore::isMethodBinding(base) |
1145 | || (AvmCore::hasGetterBinding(base) && (ne.kind == TRAIT_Getter)) |
1146 | || (AvmCore::hasSetterBinding(base) && (ne.kind == TRAIT_Setter))) { |
1147 | ensureNonFinal(basetb->getMethod(disp_id), toplevel); |
1148 | } |
1149 | MethodInfo* f = this->pool->getMethodInfo(ne.id); |
1150 | //AvmAssert(f->declaringTraits() == this); |
1151 | tb->setMethodInfo(disp_id, f); |
1152 | } |
1153 | else |
1154 | { |
1155 | // This should only be BIND_NONE if the attribute was elided via Api Versioning. |
1156 | AvmAssert(!core->isValidApiVersion(ns->getApiVersion()))do { } while (0); |
1157 | } |
1158 | break; |
1159 | } |
1160 | |
1161 | default: |
1162 | // unsupported traits type -- can't happen, caught in AbcParser::parseTraits |
1163 | AvmAssert(0)do { } while (0); |
1164 | break; |
1165 | } |
1166 | } // for i |
1167 | |
1168 | // check for sparse slot table -- anything not specified will default to * (but we must allocate space for it) |
1169 | for (uint32_t i = 0; i < tb->slotCount; i++) |
1170 | { |
1171 | if (tb->getSlotOffset(i) > 0) |
1172 | continue; |
1173 | |
1174 | #ifdef AVMPLUS_VERBOSE |
1175 | if (pool->isVerbose(VB_traits)) |
1176 | { |
1177 | core->console << "WARNING: slot " << i+1 << " on " << this << " not defined by compiler. Using *\n"; |
1178 | } |
1179 | #endif |
1180 | |
1181 | const Traitsp slotType = NULL__null; |
1182 | const uint32_t slotOffset = nextPointerSlotOffset; |
1183 | nextPointerSlotOffset += sizeof(void*); |
1184 | AvmAssert(slotOffset >= sizeof(ScriptObject))do { } while (0); |
1185 | tb->setSlotInfo(i, slotType, SST_atom, slotOffset); |
1186 | } |
1187 | if (!(next32BitSlotOffset == endOf32BitSlots && |
1188 | nextPointerSlotOffset == endOfPointerSlots && |
1189 | next64BitSlotOffset == endOf64BitSlots && |
1190 | endOf64BitSlots >= slotAreaStart)) |
1191 | { |
1192 | // Verify that we used exactly the space we predicted; if not, we may |
1193 | // have a fuzzed file that duplicate slots in certain orders. This is unacceptable, |
1194 | // as it may allow access to the same memory location with different type expectations. |
1195 | if (toplevel) |
1196 | toplevel->throwVerifyError(kCorruptABCError); |
1197 | AvmAssert(!"unhandled verify error")do { } while (0); |
1198 | } |
1199 | return endOf64BitSlots - slotAreaStart; |
1200 | } |
1201 | |
1202 | static const uint8_t* skipToInterfaceCount(const uint8_t* pos) |
1203 | { |
1204 | AvmAssert(pos != NULL)do { } while (0); |
1205 | AvmCore::skipU32(pos, 2); // skip the QName + basetraits |
1206 | const uint8_t theflags = *pos++; |
1207 | if (theflags & 8) |
1208 | AvmCore::skipU32(pos); // skip protected namespace |
1209 | return pos; |
1210 | } |
1211 | |
1212 | // Apps often have many interfaces redundantly listed, so first time thru, |
1213 | // eliminate redundant ones. We do this by only adding new interfaces to |
1214 | // the "seen" list and only traversing new super-interfaces. An interface |
1215 | // is NEW if our base class does not implement it. |
1216 | uint32_t Traits::countNewInterfaces(GCList<Traits>& seen) |
1217 | { |
1218 | // each Traits* added to this list is rooted via Domain and PoolObject, |
1219 | // so it is okay for this Stack to be opaque to the GC. |
1220 | Stack<Traits*> pending; |
1221 | pending.add(this); |
1222 | while (!pending.isEmpty()) { |
1223 | Traits* t = pending.pop(); |
1224 | const uint8_t* pos = skipToInterfaceCount(t->m_traitsPos); |
1225 | const uint32_t interfaceCount = AvmCore::readU32(pos); |
1226 | for (uint32_t j = 0; j < interfaceCount; j++) { |
1227 | // we know all interfaces have been resolved already, it is done |
1228 | // before traits construction in AbcParser::parseInstanceInfos(). |
1229 | Traitsp intf = t->pool->resolveTypeName(pos, NULL__null); |
1230 | AvmAssert(intf && intf->isInterface() && intf->base == NULL)do { } while (0); |
1231 | // an interface can "extend" multiple other interfaces, so we must recurse here. |
1232 | if ((!base || !base->subtypeof(intf)) && seen.indexOf(intf) < 0) { |
1233 | seen.add(intf); |
1234 | pending.add(intf); |
1235 | } |
1236 | } |
1237 | } |
1238 | return seen.length(); |
1239 | } |
1240 | |
1241 | static uint8_t calcLog2(uint32_t cap) |
1242 | { |
1243 | uint8_t capLog = 1; // start with at least 2 entries |
1244 | while ((1U<<capLog) < cap) |
1245 | { |
1246 | ++capLog; |
1247 | } |
1248 | AvmAssert((1U<<capLog) >= cap)do { } while (0); |
1249 | return capLog; |
1250 | } |
1251 | |
1252 | TraitsBindings* Traits::_buildTraitsBindings(const Toplevel* toplevel, boolbool includeTypes) |
1253 | { |
1254 | #ifdef AVMPLUS_VERBOSE |
1255 | if (pool->isVerbose(VB_traits)) |
1256 | { |
1257 | core->console << "Generate TraitsBindings for "<<this<<"\n"; |
1258 | } |
1259 | #endif |
1260 | |
1261 | TraitsBindings* thisData = NULL__null; |
1262 | |
1263 | // if we know the cap we need, go there right away, otherwise start at small power of 2 |
1264 | // this saves tremendously on subsequent builds of this set of bindings since we don't have to |
1265 | // waste time growing the MNHT as we build it |
1266 | const int32_t bindingCap = m_bindingCapLog2 ? (1 << m_bindingCapLog2) : 2; |
1267 | |
1268 | MMgc::GC* gc = core->GetGC(); |
1269 | MultinameBindingHashtable* bindings = MultinameBindingHashtable::create(gc, bindingCap); |
1270 | AvmAssert(bindings->numQuads == bindingCap)do { } while (0); |
1271 | |
1272 | if (this->posType() == TRAITSTYPE_CATCH) |
1273 | { |
1274 | const uint8_t* pos = m_traitsPos; |
1275 | |
1276 | Traits* t = this->pool->resolveTypeName(pos, toplevel); |
1277 | |
1278 | // this assumes we save name/ns in all builds, not just verbose |
1279 | NamespaceSetp nss = NamespaceSet::create(core->GetGC(), this->ns()); |
1280 | NamespaceSetp compat_nss = nss; |
1281 | addVersionedBindings(bindings, this->name(), compat_nss, AvmCore::makeSlotBinding(0, BKIND_VAR)); |
1282 | // bindings just need room for one slot binding |
1283 | thisData = TraitsBindings::alloc(gc, this, /*base*/NULL__null, bindings, /*slotCount*/1, /*methodCount*/0, truetrue); |
1284 | thisData->setSlotInfo(0, t, slotStorageType(getBuiltinType(t)), this->m_sizeofInstance); |
1285 | thisData->m_slotSize = is8ByteSlot(t) ? 8 : 4; |
1286 | } |
1287 | else |
1288 | { |
1289 | TraitsBindingsp basetb = this->base ? this->base->getTraitsBindings() : NULL__null; |
1290 | |
1291 | // Copy protected traits from base class into new protected namespace |
1292 | if (basetb && base->protectedNamespace && this->protectedNamespace) |
1293 | { |
1294 | StTraitsBindingsIterator iter(basetb); |
1295 | while (iter.next()) |
1296 | { |
1297 | if (!iter.key()) continue; |
1298 | if (iter.ns() == base->protectedNamespace) |
1299 | { |
1300 | bindings->add(iter.key(), this->protectedNamespace, iter.value()); |
1301 | } |
1302 | } |
1303 | } |
1304 | |
1305 | SlotSizeInfo _slotSizeInfo; |
1306 | SlotSizeInfo* slotSizeInfo = includeTypes ? &_slotSizeInfo : NULL__null; |
1307 | |
1308 | uint32_t slotCount = 0; |
1309 | uint32_t methodCount = 0; |
1310 | buildBindings(basetb, bindings, slotCount, methodCount, slotSizeInfo, toplevel); |
1311 | |
1312 | thisData = TraitsBindings::alloc(gc, this, basetb, bindings, slotCount, methodCount, includeTypes); |
1313 | if (slotSizeInfo) |
1314 | { |
1315 | thisData->m_slotSize = finishSlotsAndMethods(basetb, thisData, toplevel, *slotSizeInfo); |
1316 | if (basetb) |
1317 | thisData->m_slotSize += basetb->m_slotSize; |
1318 | } |
1319 | |
1320 | if (!isInterface() && m_implementsNewInterfaces) { |
1321 | // fix up interface bindings |
1322 | for (InterfaceIterator it(this); it.hasNext(); ) { |
1323 | Traits* t = it.next(); |
1324 | if (!base || !base->subtypeof(t)) { |
1325 | // new interface not implemented in base. |
1326 | thisData->fixOneInterfaceBindings(t); |
1327 | } |
1328 | } |
1329 | } |
1330 | } |
1331 | |
1332 | // hashtable (if we have one) must start on pointer-sized boundary... |
1333 | // much easier to always round slotsize up unconditionally rather than |
1334 | // only for cases with hashtable, so that's what we'll do. (MMgc currently |
1335 | // allocate in 8-byte increments anyway, so we're not really losing any space.) |
1336 | // (only really necessary to do this if we calced slotSizeInfo, but simpler & harmless |
1337 | // to just do it unconditionally) |
1338 | thisData->m_slotSize = (thisData->m_slotSize + (sizeof(uintptr_t)-1)) & ~(sizeof(uintptr_t)-1); |
1339 | |
1340 | // remember the cap we need |
1341 | if (m_bindingCapLog2 == 0) |
1342 | m_bindingCapLog2 = calcLog2(thisData->m_bindings->numQuads); // remember capacity, not count |
1343 | AvmAssert(m_bindingCapLog2 > 0)do { } while (0); |
1344 | |
1345 | #ifdef AVMPLUS_VERBOSE |
1346 | if (pool->isVerbose(VB_traits)) |
1347 | { |
1348 | core->console << this << " bindings\n"; |
1349 | StTraitsBindingsIterator iter(thisData); |
1350 | while (iter.next()) |
1351 | { |
1352 | core->console << iter.key() << ":" << (uint32_t)(uintptr_t)(iter.value()) << "\n"; |
1353 | } |
1354 | core->console << this << " end bindings \n"; |
1355 | } |
1356 | #endif |
1357 | |
1358 | AvmAssert(m_tbref->isNull())do { } while (0); |
1359 | m_tbref = thisData->GetWeakRef(); |
1360 | core->tbCache()->add(thisData); |
1361 | return thisData; |
1362 | } |
1363 | |
1364 | TraitsMetadata* Traits::_buildTraitsMetadata() |
1365 | { |
1366 | #ifdef AVMPLUS_VERBOSE |
1367 | if (pool->isVerbose(VB_traits)) |
1368 | { |
1369 | core->console << "Generate TraitsMetadata for "<<this<<"\n"; |
1370 | } |
1371 | #endif |
1372 | TraitsBindingsp td = this->getTraitsBindings(); |
1373 | |
1374 | MMgc::GC* gc = core->GetGC(); |
1375 | |
1376 | TraitsMetadatap basetm = this->base ? this->base->getTraitsMetadata() : NULL__null; |
1377 | |
1378 | const uint32_t extra = td->slotCount * sizeof(TraitsMetadata::MetadataPtr) + td->methodCount * sizeof(TraitsMetadata::MetadataPtr); |
1379 | |
1380 | TraitsMetadata* tm = TraitsMetadata::create(gc, extra, basetm, this->pool, this->metadata_pos, td->slotCount, td->methodCount); |
1381 | tm->slotMetadataPos = (TraitsMetadata::MetadataPtr*)(tm + 1); |
1382 | tm->methodMetadataPos = (TraitsMetadata::MetadataPtr*)(tm->slotMetadataPos + tm->slotCount); |
1383 | |
1384 | const uint8_t* pos = traitsPosStart(); |
1385 | const uint32_t nameCount = pos ? AvmCore::readU32(pos) : 0; |
1386 | SlotIdCalcer sic(td->base ? td->base->slotCount : 0, this->allowEarlyBinding()); |
1387 | NameEntry ne; |
1388 | for (uint32_t i = 0; i < nameCount; i++) |
1389 | { |
1390 | ne.readNameEntry(pos); |
1391 | switch (ne.kind) |
1392 | { |
1393 | case TRAIT_Class: |
1394 | AvmAssert(0)do { } while (0); |
1395 | // classes shouldn't have metadata, but just fall thru just in case |
1396 | case TRAIT_Slot: |
1397 | case TRAIT_Const: |
1398 | { |
1399 | const uint32_t slot_id = sic.calc_id(ne.id); |
1400 | if (ne.tag & ATTR_metadata) |
1401 | tm->slotMetadataPos[slot_id] = ne.meta_pos; |
1402 | break; |
1403 | } |
1404 | case TRAIT_Getter: |
1405 | case TRAIT_Setter: |
1406 | case TRAIT_Method: |
1407 | { |
1408 | if (ne.tag & ATTR_metadata) |
1409 | { |
1410 | Multiname qn; |
1411 | // passing NULL for toplevel here, since it's only used if a verification error occurs -- |
1412 | // but if there was one, we would have encountered it during AbcParser::parseTraits. |
1413 | this->pool->resolveBindingNameNoCheck(ne.qni, qn, /*toplevel*/NULL__null); |
1414 | const Binding b = td->findBinding(qn.getName(), qn.getNamespace()); |
1415 | AvmAssert(b != BIND_NONE)do { } while (0); |
1416 | const uint32_t disp_id = uint32_t(uintptr_t(b) >> 3) + (ne.kind == TRAIT_Setter); |
1417 | tm->methodMetadataPos[disp_id] = ne.meta_pos; |
1418 | } |
1419 | break; |
1420 | } |
1421 | |
1422 | default: |
1423 | // unsupported traits type -- can't happen, caught in AbcParser::parseTraits |
1424 | AvmAssert(0)do { } while (0); |
1425 | break; |
1426 | } |
1427 | } // for i |
1428 | |
1429 | AvmAssert(m_tmref->isNull())do { } while (0); |
1430 | m_tmref = tm->GetWeakRef(); |
1431 | core->tmCache()->add(tm); |
1432 | return tm; |
1433 | } |
1434 | |
1435 | /** |
1436 | * add t to pending[] ahead of any existing entries that are |
1437 | * subtypes or implementers of t, so that when we iterate through |
1438 | * pending[] we will traverse the inheritance DAG top-down. |
1439 | */ |
1440 | void insertSupertype(Traits* t, GCList<Traits> &pending) |
1441 | { |
1442 | uint32_t i = 0; |
1443 | for (uint32_t n = pending.length(); i < n; i++) { |
1444 | if (pending[i]->subtypeof(t)) { |
1445 | pending.insert(i, t); |
1446 | return; |
1447 | } |
1448 | } |
1449 | pending.add(t); |
1450 | } |
1451 | |
1452 | /** |
1453 | * resolve this traits signatures by visiting all supertypes in |
1454 | * top-down toplogical order, then resolving our own signatures. The top-down |
1455 | * order ensures that we resolve base classes and interfaces first, while |
1456 | * avoiding recursion. |
1457 | */ |
1458 | void Traits::resolveSignatures(const Toplevel* toplevel) |
1459 | { |
1460 | // toplevel actually can be null, when resolving the builtin classes... |
1461 | // but they should never cause verification errors in functioning builds |
1462 | if (m_resolved) |
1463 | return; |
1464 | |
1465 | GCList<Traits> pending(core->gc, kListInitialCapacity); |
1466 | // copy primary supertypes into pending. |
1467 | // primary_supertypes[] is already in top-down order. |
1468 | for (int32_t i = 0; i < MAX_PRIMARY_SUPERTYPE; i++) { |
1469 | Traits* t = m_primary_supertypes[i]; |
1470 | if (t == NULL__null || t == this) |
1471 | break; |
1472 | if (!t->m_resolved) |
1473 | pending.add(t); |
1474 | } |
1475 | |
1476 | // copy other base types, and interfaces, into pending[] by |
1477 | // and maintaining the partial ordering that each type's bases and |
1478 | // interfaces are visited before that type itself is visited. |
1479 | for (Traits** st = m_secondary_supertypes; *st != NULL__null; st++) { |
1480 | Traits* t = *st; |
1481 | if (t != this && !t->m_resolved) |
1482 | insertSupertype(t, pending); |
1483 | } |
1484 | |
1485 | for (uint32_t i = 0, n = pending.length(); i < n; i++) { |
1486 | AvmAssert(!pending[i]->m_resolved)do { } while (0); |
1487 | pending[i]->resolveSignaturesSelf(toplevel); |
1488 | } |
1489 | |
1490 | this->resolveSignaturesSelf(toplevel); |
1491 | } |
1492 | |
1493 | void Traits::verifyBindings(const Toplevel* toplevel) |
1494 | { |
1495 | // builtin Traits get called before a Toplevel is created, so they |
1496 | // get a free pass. Everyone else better pass a good value. |
1497 | AvmAssert(toplevel != NULL || pool->isBuiltin)do { } while (0); |
1498 | |
1499 | #ifdef DEBUG |
1500 | AvmAssert(!m_bindingsVerified)do { } while (0); |
1501 | // make sure our supertypes have verified their bindings. (must be done before calling _buildTraitsBindings) |
1502 | for (Traits* t = this->base; t != NULL__null; t = t->base) |
1503 | AvmAssert(t->m_bindingsVerified)do { } while (0); |
1504 | for (Traits** st = this->m_secondary_supertypes; *st != NULL__null; st++) |
1505 | AvmAssert(*st == this || (*st)->m_bindingsVerified)do { } while (0); |
1506 | #endif |
1507 | |
1508 | AvmAssert(m_tbref == core->GetGC()->emptyWeakRef)do { } while (0); |
1509 | // force buildTraitsBindings to run now, so that any errors will |
1510 | // also be reported now, instead of later when called on a QCache |
1511 | // miss, when we don't have a valid Toplevel* for exception-throwing context. |
1512 | _buildTraitsBindings(toplevel, /*includeTypes*/falsefalse); |
1513 | #ifdef DEBUG |
1514 | this->m_bindingsVerified = truetrue; |
1515 | #endif |
1516 | } |
1517 | |
1518 | /** |
1519 | * This must be called before any method is verified or any |
1520 | * instances are created. It is not done eagerly in AbcParser |
1521 | * because doing so would prevent circular type references between |
1522 | * slots of cooperating classes. |
1523 | * |
1524 | * Resolve the type and position/width of each slot. |
1525 | */ |
1526 | void Traits::resolveSignaturesSelf(const Toplevel* toplevel) |
1527 | { |
1528 | // builtin Traits get called before a Toplevel is created, so they |
1529 | // get a free pass. Everyone else better pass a good value. |
1530 | AvmAssert(toplevel != NULL || pool->isBuiltin)do { } while (0); |
1531 | |
1532 | AvmAssert(m_bindingsVerified)do { } while (0); |
1533 | |
1534 | #ifdef DEBUG |
1535 | AvmAssert(!m_resolved)do { } while (0); |
1536 | // make sure our supertypes are resolved. (must be done before calling _buildTraitsBindings) |
1537 | for (Traits* t = this->base; t != NULL__null; t = t->base) |
1538 | AvmAssert(t->m_resolved)do { } while (0); |
1539 | for (Traits** st = this->m_secondary_supertypes; *st != NULL__null; st++) |
1540 | AvmAssert(*st == this || (*st)->m_resolved)do { } while (0); |
1541 | #endif |
1542 | |
1543 | // we might already have one -- if so, toss it |
1544 | m_tbref = core->GetGC()->emptyWeakRef; |
1545 | TraitsBindings* tb = _buildTraitsBindings(toplevel, /*includeTypes*/truetrue); |
1546 | genInitBody(toplevel, tb); |
1547 | |
1548 | // leave m_tmref as empty, we don't need it yet |
1549 | |
1550 | uint32_t slotAreaSize = 0; |
1551 | uint32_t slotAreaCount = 0; |
1552 | |
1553 | switch (posType()) |
1554 | { |
1555 | case TRAITSTYPE_INTERFACE: |
1556 | case TRAITSTYPE_INSTANCE: |
1557 | case TRAITSTYPE_CLASS: |
1558 | case TRAITSTYPE_SCRIPT: |
1559 | case TRAITSTYPE_ACTIVATION: |
1560 | case TRAITSTYPE_CATCH: |
1561 | computeNonNativeSlotAreaCountAndSize(tb, slotAreaCount, slotAreaSize); |
1562 | m_totalSize = getSizeOfInstance() + slotAreaSize; |
1563 | break; |
1564 | case TRAITSTYPE_NVA: |
1565 | case TRAITSTYPE_RT: |
1566 | m_totalSize = getSizeOfInstance(); |
1567 | break; |
1568 | } |
1569 | AvmAssert(m_totalSize >= m_sizeofInstance)do { } while (0); |
1570 | if (m_needsHashtable || (base && base->base && base->m_hashTableOffset && !isXMLType())) |
1571 | { |
1572 | // round up total size to multiple of pointer size. |
1573 | m_totalSize = ((m_totalSize+(sizeof(uintptr_t)-1))&~(sizeof(uintptr_t)-1)); |
1574 | m_hashTableOffset = m_totalSize; |
1575 | m_totalSize += sizeof(InlineHashtable); |
1576 | AvmAssert(builtinType == BUILTIN_boolean ? true : (m_hashTableOffset & 3) == 0)do { } while (0); |
1577 | AvmAssert((m_hashTableOffset & (sizeof(uintptr_t)-1)) == 0)do { } while (0); |
1578 | AvmAssert((m_totalSize & (sizeof(uintptr_t)-1)) == 0)do { } while (0); |
1579 | } |
1580 | |
1581 | // make sure all the methods have resolved types |
1582 | if (tb->methodCount) |
1583 | { |
1584 | const TraitsBindings::BindingMethodInfo* tbm = tb->getMethods(); |
1585 | const TraitsBindings::BindingMethodInfo* tbm_end = tbm + tb->methodCount; |
1586 | for ( ; tbm < tbm_end; ++tbm) |
1587 | { |
1588 | // don't assert: could be null if only one of a get/set pair is implemented |
1589 | //AvmAssert(tbm->f != NULL); |
1590 | if (tbm->f != NULL__null) |
1591 | { |
1592 | tbm->f->resolveSignature(toplevel); |
1593 | } |
1594 | } |
1595 | } |
1596 | |
1597 | if (this->init != NULL__null) |
1598 | this->init->resolveSignature(toplevel); |
1599 | |
1600 | boolbool legal = truetrue; |
1601 | TraitsBindingsp tbbase = tb->base; // might be null |
1602 | |
1603 | if (tbbase && tbbase->methodCount > 0) |
1604 | { |
1605 | // check concrete overrides |
1606 | const TraitsBindings::BindingMethodInfo* basetbm = tbbase->getMethods(); |
1607 | const TraitsBindings::BindingMethodInfo* basetbm_end = basetbm + tbbase->methodCount; |
1608 | const TraitsBindings::BindingMethodInfo* tbm = tb->getMethods(); |
1609 | for ( ; basetbm < basetbm_end; ++basetbm, ++tbm) |
1610 | { |
1611 | if (basetbm->f != NULL__null && basetbm->f != tbm->f) |
1612 | legal &= tb->checkOverride(core, basetbm->f, tbm->f); |
1613 | } |
1614 | } |
1615 | |
1616 | if (legal && !this->isInterface()) |
1617 | { |
1618 | legal &= tb->checkLegalInterfaces(core); |
1619 | } |
1620 | |
1621 | if (!legal) |
1622 | { |
1623 | AvmAssert(!m_resolved)do { } while (0); |
1624 | Multiname qname(ns(), name()); |
1625 | if (toplevel) |
1626 | toplevel->throwVerifyError(kIllegalOverrideError, core->toErrorString(&qname), core->toErrorString(this)); |
1627 | #ifdef VMCFG_VERIFYALL |
1628 | else { |
1629 | if (core->config.verifyonly) |
1630 | core->console << "ILLEGAL OVERRIDE\n"; |
1631 | } |
1632 | #endif |
1633 | AvmAssert(!"unhandled verify error")do { } while (0); |
1634 | } |
1635 | |
1636 | tb->buildSlotDestroyInfo(core->GetGC(), m_slotDestroyInfo, slotAreaCount, slotAreaSize); |
1637 | |
1638 | m_resolved = truetrue; |
1639 | #ifdef AVMPLUS_VERBOSE |
1640 | if (pool->isVerbose(VB_traits)) { |
1641 | core->console << "Resolved "; |
1642 | printExtended(core->console) << "\n"; |
1643 | } |
1644 | #endif |
1645 | } |
1646 | |
1647 | #ifdef AVMPLUS_VERBOSE |
1648 | PrintWriter& Traits::printExtended(PrintWriter& pw) |
1649 | { |
1650 | const char* desc = NULL__null; |
1651 | switch (posType()) { |
1652 | case TRAITSTYPE_INTERFACE: |
1653 | desc = "interface"; break; |
1654 | case TRAITSTYPE_INSTANCE: |
1655 | desc = "instance"; break; |
1656 | case TRAITSTYPE_CLASS: |
1657 | desc = "class"; break; |
1658 | case TRAITSTYPE_SCRIPT: |
1659 | desc = "script"; break; |
1660 | case TRAITSTYPE_ACTIVATION: |
1661 | desc = "activation"; break; |
1662 | case TRAITSTYPE_CATCH: |
1663 | desc = "catch"; break; |
1664 | case TRAITSTYPE_NVA: // null/void/any |
1665 | desc = "singleton"; break; |
1666 | case TRAITSTYPE_RT: |
1667 | desc = "rt"; break; |
1668 | default: |
1669 | AvmAssert(false)do { } while (0); |
1670 | } |
1671 | const char* domainString = core->identifyDomain(pool->domain); |
1672 | pw << ns() << "::" << name() << ", type:" << desc << ",domain:"; |
1673 | if (domainString) { |
1674 | pw << domainString; |
1675 | } else { |
1676 | // not a well known domain, print the address to help distinguish distinct domains |
1677 | pw << "@" << hexAddr((intptr_t)(Domain*)pool->domain); |
1678 | } |
1679 | return pw; |
1680 | } |
1681 | |
1682 | #endif |
1683 | |
1684 | |
1685 | #ifdef VMCFG_AOT |
1686 | static inline void hookUpActivationTraitsInitMethodForTraitsMethod(AvmCore* core, MethodInfo* m) |
1687 | { |
1688 | AvmAssert(m->needActivation())do { } while (0); |
1689 | |
1690 | const AOTInfo* aotInfo = m->pool()->aotInfo; |
1691 | Traits* activationTraits = m->activationTraits(); |
1692 | AvmAssert(activationTraits != NULL)do { } while (0); |
1693 | AvmAssert(m->method_id() < aotInfo->nActivationTraits)do { } while (0); |
1694 | AvmAssert(aotInfo->activationTraits != NULL)do { } while (0); |
1695 | AvmAssert(aotInfo->activationTraits[m->method_id()] == activationTraits)do { } while (0); |
1696 | AvmAssert(aotInfo->activationInfo != NULL)do { } while (0); |
1697 | |
1698 | // See comment in initActivationTraits about why this can be called more than once per Traits |
1699 | if (activationTraits->init == NULL__null) { |
1700 | if (aotInfo->activationInfo[m->method_id()].initHandler) { |
1701 | // NativeMethodInfo.handler is a union of |
1702 | // pointer to function and pointer to member function. |
1703 | // Zero the structure so that the entire thing is |
1704 | // initialized. |
1705 | // See bugzilla#647660 |
1706 | NativeMethodInfo compiledMethodInfo = {0}; |
1707 | compiledMethodInfo.thunker = aotThunker; |
1708 | AvmThunkNativeHandler nhandler; |
1709 | nhandler.function = aotInfo->activationInfo[m->method_id()].initHandler; |
1710 | activationTraits->init = MethodInfo::create(core->GetGC(), MethodInfo::kInitMethodStub, activationTraits, &compiledMethodInfo, nhandler, aotInfo->activationInfo[m->method_id()].initMethodId); |
1711 | } |
1712 | // The following comes from Verifier::write() TODO: refactor so we can share this code |
1713 | const ScopeTypeChain *scope = m->activationScope(); |
1714 | if (scope == NULL__null) { |
1715 | scope = m->declaringScope()->cloneWithNewTraits(core->GetGC(), activationTraits); |
1716 | activationTraits->setDeclaringScopes(scope); |
1717 | m->init_activationScope(scope); |
1718 | } |
1719 | } |
1720 | } |
1721 | |
1722 | void Traits::initActivationTraits() |
1723 | { |
1724 | // Note: this can be called multiple times per Traits from initScript, which must call this in case it's needed |
1725 | // but is itself called once per Toplevel |
1726 | const uint8_t* pos = traitsPosStart(); |
1727 | |
1728 | if (this->init->needActivation()) { |
1729 | hookUpActivationTraitsInitMethodForTraitsMethod(core, this->init); |
1730 | } |
1731 | |
1732 | NameEntry ne; |
1733 | const uint32_t nameCount = pos ? AvmCore::readU32(pos) : 0; |
1734 | for (uint32_t i = 0; i < nameCount; i++) |
1735 | { |
1736 | ne.readNameEntry(pos); |
1737 | |
1738 | switch (ne.kind) |
1739 | { |
1740 | case TRAIT_Slot: |
1741 | case TRAIT_Const: |
1742 | case TRAIT_Class: |
1743 | break; |
1744 | case TRAIT_Method: |
1745 | case TRAIT_Getter: |
1746 | case TRAIT_Setter: |
1747 | { |
1748 | MethodInfo* m = pool->getMethodInfo(ne.id); |
1749 | if (m->needActivation()) { |
1750 | hookUpActivationTraitsInitMethodForTraitsMethod(core, m); |
1751 | } |
1752 | break; |
1753 | } |
1754 | default: |
1755 | // unsupported traits type -- can't happen, caught in AbcParser::parseTraits |
1756 | AvmAssert(0)do { } while (0); |
1757 | break; |
1758 | } |
1759 | } |
1760 | } |
1761 | #endif |
1762 | |
1763 | // static |
1764 | boolbool Traits::isMachineCompatible(const Traits* a, const Traits* b) |
1765 | { |
1766 | return (a == b) || |
1767 | // *, Object, and Void are each represented as Atom |
1768 | ((!a || a->builtinType == BUILTIN_object || a->builtinType == BUILTIN_void) && |
1769 | (!b || b->builtinType == BUILTIN_object || b->builtinType == BUILTIN_void)) || |
1770 | // all other non-pointer types have unique representations |
1771 | (a && b && !a->isMachineType() && !b->isMachineType()); |
1772 | } |
1773 | |
1774 | PrintWriter& Traits::print(PrintWriter& prw, boolbool includeAllNamespaces) const |
1775 | { |
1776 | if (name() != NULL__null) { |
1777 | Multiname::print(prw, ns(), name(), falsefalse, !includeAllNamespaces); |
1778 | } else { |
1779 | prw << "Traits@" << asAtomHex((Atom)this); |
1780 | } |
1781 | return prw; |
1782 | } |
1783 | |
1784 | void Traits::genInitBody(const Toplevel* toplevel, TraitsBindings* tb) |
1785 | { |
1786 | #if defined(VMCFG_AOT) |
1787 | if (this->init && this->init->isAotCompiled()) |
1788 | return; |
1789 | #endif |
1790 | |
1791 | struct ValidateInitVisitor: public InitVisitor |
1792 | { |
1793 | boolbool hasDefaults; |
1794 | ValidateInitVisitor() : hasDefaults(falsefalse) {} |
1795 | virtual ~ValidateInitVisitor() {} |
1796 | void defaultVal(Atom, uint32_t, Traits*) |
1797 | { |
1798 | hasDefaults = truetrue; |
1799 | } |
1800 | }; |
1801 | ValidateInitVisitor visitor; |
1802 | visitInitBody(&visitor, toplevel, tb); |
1803 | |
1804 | // if initialization code gen is required, create a new method body and write it to traits->init->body_pos |
1805 | if (visitor.hasDefaults && !this->init) |
1806 | { |
1807 | // Make a do-nothing init method. |
1808 | this->init = MethodInfo::create(core->gc, MethodInfo::kInitMethodStub, this); |
1809 | AvmAssert(this->init->declaringTraits() == this)do { } while (0); |
1810 | static const uint8_t body[] = { |
1811 | 2, // max_stack |
1812 | 1, // local_count |
1813 | 1, // init_scope_depth |
1814 | 1, // max_scope_depth |
1815 | 1, // code_length |
1816 | (uint8_t)OP_returnvoid, // code |
1817 | 0 // exception_count |
1818 | // the verifier and interpreter don't read the activation traits so stop here |
1819 | }; |
1820 | |
1821 | // The synthetic MethodInfo points directly to the static const ABC above. |
1822 | this->init->set_abc_body_pos(body); |
1823 | } |
1824 | } |
1825 | |
1826 | void Traits::visitInitBody(InitVisitor *visitor, const Toplevel* toplevel, const TraitsBindings* tb) |
1827 | { |
1828 | const uint8_t* pos = traitsPosStart(); |
1829 | SlotIdCalcer sic(tb->base ? tb->base->slotCount : 0, this->allowEarlyBinding()); |
1830 | NameEntry ne; |
1831 | const uint32_t nameCount = pos ? AvmCore::readU32(pos) : 0; |
1832 | for (uint32_t i = 0; i < nameCount; i++) |
1833 | { |
1834 | ne.readNameEntry(pos); |
1835 | switch (ne.kind) |
1836 | { |
1837 | case TRAIT_Slot: |
1838 | case TRAIT_Const: |
1839 | case TRAIT_Class: |
1840 | { |
1841 | uint32_t slotid = sic.calc_id(ne.id); |
1842 | // note, for TRAIT_Class, AbcParser::parseTraits has already verified that pool->cinits[ne.info] is not null |
1843 | Traitsp slotType = (ne.kind == TRAIT_Class) ? |
1844 | pool->getClassTraits(ne.info) : |
1845 | pool->resolveTypeName(ne.info, NULL__null); |
1846 | uint32_t value_index = ne.value_index; |
1847 | CPoolKind kind = ne.value_kind; |
1848 | Atom value = pool->getLegalDefaultValue(toplevel, value_index, kind, slotType); |
1849 | switch (Traits::getBuiltinType(slotType)) |
1850 | { |
1851 | case BUILTIN_any: |
1852 | case BUILTIN_object: |
1853 | if (value == 0) |
1854 | continue; |
1855 | break; |
1856 | case BUILTIN_number: |
1857 | if (AvmCore::number_d(value) == 0) |
1858 | continue; |
1859 | break; |
1860 | case BUILTIN_boolean: |
1861 | AvmAssert((uintptr_t(falseAtom)>>3) == 0)do { } while (0); |
1862 | if (value == falseAtom) |
1863 | continue; |
1864 | AvmAssert(value == trueAtom)do { } while (0); |
1865 | break; |
1866 | case BUILTIN_uint: |
1867 | case BUILTIN_int: |
1868 | if (value == (zeroIntAtom)) |
1869 | continue; |
1870 | break; |
1871 | case BUILTIN_namespace: |
1872 | case BUILTIN_string: |
1873 | default: |
1874 | if (AvmCore::isNull(value)) |
1875 | continue; |
1876 | break; |
1877 | } |
1878 | visitor->defaultVal(value, slotid, slotType); |
1879 | break; |
1880 | } |
1881 | } |
1882 | } // for i |
1883 | } |
1884 | |
1885 | void Traits::destroyInstance(ScriptObject* obj) const |
1886 | { |
1887 | AvmAssert(m_resolved)do { } while (0); |
1888 | |
1889 | InlineHashtable* ht = m_hashTableOffset ? obj->getTableNoInit() : NULL__null; |
1890 | |
1891 | // start by clearing native space to zero (except baseclasses) |
1892 | union { |
1893 | char* p_8; |
1894 | uint32_t* p; |
1895 | }; |
1896 | p_8 = (char*)obj + sizeof(AvmPlusScriptableObject); |
1897 | AvmAssert((uintptr_t(p) % sizeof(uint32_t)) == 0)do { } while (0); |
1898 | |
1899 | if (!m_slotDestroyInfo.test(0)) |
1900 | { |
1901 | AvmAssert(m_slotDestroyInfo.cap() == 1)do { } while (0); |
1902 | AvmAssert(m_totalSize >= (sizeof(AvmPlusScriptableObject) + (ht ? sizeof(InlineHashtable) : 0)))do { } while (0); |
1903 | uint32_t sizeToZero = m_totalSize - (sizeof(AvmPlusScriptableObject) + (ht ? sizeof(InlineHashtable) : 0)); |
1904 | AvmAssert((sizeToZero % sizeof(uint32_t)) == 0)do { } while (0); // we assume all sizes are multiples of sizeof(uint32_t) |
1905 | |
1906 | // no RCObjects, so just zero it all... my, that was easy |
1907 | VMPI_memset::memset(p, 0, sizeToZero); |
1908 | } |
1909 | else |
1910 | { |
1911 | uint32_t sizeToZero = m_sizeofInstance - uint32_t(sizeof(AvmPlusScriptableObject)); |
1912 | AvmAssert((sizeToZero % sizeof(uint32_t)) == 0)do { } while (0); // we assume all sizes are multiples of sizeof(uint32_t) |
1913 | VMPI_memset::memset(p, 0, sizeToZero); |
1914 | p += (sizeToZero / sizeof(uint32_t)); |
1915 | |
1916 | AvmAssert(m_slotDestroyInfo.cap() >= 1)do { } while (0); |
1917 | AvmAssert((uintptr_t(p) % sizeof(uint32_t)) == 0)do { } while (0); |
1918 | |
1919 | const uint32_t slotAreaSize = getSlotAreaSize(); |
1920 | const uint32_t bitsUsed = slotAreaSize / sizeof(uint32_t); // not sizeof(Atom)! |
1921 | for (uint32_t bit = 1; bit <= bitsUsed; bit++) |
1922 | { |
1923 | if (m_slotDestroyInfo.test(bit)) |
1924 | { |
1925 | #ifdef AVMPLUS_64BIT |
1926 | AvmAssert((uintptr_t(p) & 7) == 0)do { } while (0); // we had better be on an 8-byte boundary... |
1927 | #endif |
1928 | Atom a = *(const Atom*)p; |
1929 | RCObject* rc = NULL__null; |
1930 | if (atomKind(a)((avmplus::Atom)((uintptr_t(a) & 7))) <= kNamespaceType) |
1931 | { |
1932 | rc = (RCObject*)atomPtr(a)((void*)(uintptr_t(a) & ~7)); |
1933 | if (rc) |
1934 | { |
1935 | AvmAssert(GC::GetGC(obj)->IsRCObject(rc))do { } while (0); |
1936 | rc->DecrementRef(); |
1937 | } |
1938 | } |
1939 | } |
1940 | *p++ = 0; |
1941 | } |
1942 | } |
1943 | |
1944 | // finally, zap the hashtable (if any) |
1945 | if(ht) |
1946 | { |
1947 | ht->destroy(); |
1948 | } |
1949 | //for DictionaryObject also zero out the |
1950 | //hashtable pointer stored at the offset address; |
1951 | if(isDictionary()) |
1952 | { |
1953 | union { |
1954 | char* p_8; |
1955 | uintptr_t* ptr; |
1956 | }; |
1957 | p_8 = (char*)obj + m_hashTableOffset; |
1958 | *ptr = 0; |
1959 | } |
1960 | } |
1961 | |
1962 | void Traits::traceSlots(MMgc::GC* gc, ScriptObject* obj) const |
1963 | { |
1964 | if (m_slotDestroyInfo.test(0)) |
1965 | { |
1966 | const uint32_t slotAreaSize = getSlotAreaSize(); |
1967 | union { |
1968 | char* p_8; |
1969 | uint32_t* p; |
1970 | }; |
1971 | p_8 = (char*)obj + m_sizeofInstance; |
1972 | |
1973 | AvmAssert(m_slotDestroyInfo.cap() >= 1)do { } while (0); |
1974 | AvmAssert((uintptr_t(p) & 3) == 0)do { } while (0); |
1975 | const uint32_t bitsUsed = slotAreaSize / sizeof(uint32_t); // not sizeof(Atom)! |
1976 | |
1977 | traceSlotsFromBitmap(gc, p, m_slotDestroyInfo, bitsUsed); |
1978 | } |
1979 | } |
1980 | |
1981 | // OPTIMIZEME: We can do better than traceInfo.test, which is probably |
1982 | // going to be about a dozen instructions on interesting architectures. |
1983 | // A fairly trivial optimization would be to provide an API on FixedBitSet |
1984 | // that provides uint32_t values representing sets of 32 bits, and |
1985 | // make the loop here doubly nested. That would also allow us to skip |
1986 | // quickly areas of objects that have all non-pointer data. |
1987 | |
1988 | void Traits::traceSlotsFromBitmap(MMgc::GC* gc, uint32_t* p, const FixedBitSet& traceInfo, uint32_t bitsUsed) const |
1989 | { |
1990 | for (uint32_t bit = 1; bit <= bitsUsed; bit++) |
1991 | { |
1992 | if (traceInfo.test(bit)) |
1993 | { |
1994 | #ifdef AVMPLUS_64BIT |
1995 | AvmAssert((uintptr_t(p) & 7) == 0)do { } while (0); // we had better be on an 8-byte boundary... |
1996 | #endif |
1997 | avmplus::Atom a = *(const avmplus::Atom*)p; |
1998 | if (atomKind(a)((avmplus::Atom)((uintptr_t(a) & 7))) <= avmplus::AtomConstants::kNamespaceType) |
1999 | { |
2000 | // This includes untagged pointers to String, Namespace, ScriptObject. |
2001 | AvmAssert(atomPtr(a) == NULL || gc->IsRCObject(atomPtr(a)))do { } while (0); |
2002 | gc->TracePointer(atomPtr(a)((void*)(uintptr_t(a) & ~7)) HEAP_GRAPH_ARG((uintptr_t*)p)); |
2003 | } |
2004 | else if (atomKind(a)((avmplus::Atom)((uintptr_t(a) & 7))) == avmplus::AtomConstants::kDoubleType) |
2005 | { |
2006 | AvmAssert(atomPtr(a) != NULL)do { } while (0); |
2007 | gc->TracePointer(atomPtr(a)((void*)(uintptr_t(a) & ~7)) HEAP_GRAPH_ARG((uintptr_t*)p)); |
2008 | } |
2009 | } |
2010 | p++; |
2011 | } |
2012 | } |
2013 | |
2014 | Stringp Traits::formatClassName() |
2015 | { |
2016 | #ifdef VMCFG_CACHE_GQCN |
2017 | if (_fullname != NULL__null) |
2018 | return _fullname; |
2019 | #endif |
2020 | |
2021 | Multiname qname(ns(), name()); |
2022 | qname.setQName(); |
2023 | StringBuffer buffer(core); |
2024 | buffer << qname; |
2025 | int length = buffer.length(); |
2026 | if (length && buffer.c_str()[length-1] == '$') |
2027 | { |
2028 | length--; |
2029 | } |
2030 | #ifndef VMCFG_CACHE_GQCN |
2031 | Stringp _fullname; |
2032 | #endif |
2033 | _fullname = core->newStringUTF8(buffer.c_str(), length); |
2034 | return _fullname; |
2035 | } |
2036 | |
2037 | |
2038 | Binding Traits::getOverride(TraitsBindingsp basetb, Namespacep ns, Stringp name, int tag, const Toplevel* toplevel) const |
2039 | { |
2040 | Binding baseBinding = BIND_NONE; |
2041 | if (base) |
2042 | { |
2043 | const Namespacep lookupNS = (protectedNamespace == ns && base->protectedNamespace) ? (Namespacep)base->protectedNamespace : ns; |
2044 | AvmAssert(basetb != NULL)do { } while (0); |
2045 | baseBinding = basetb->findBinding(name, lookupNS); |
2046 | } |
2047 | const BindingKind baseBindingKind = AvmCore::bindingKind(baseBinding); |
2048 | |
2049 | const TraitKind kind = TraitKind(tag & 0x0f); |
2050 | // some extra-picky compilers will complain about the values being out of |
2051 | // range for the comparison (if we use "kind") even with explicit int casting. |
2052 | // so recycle the expression for the assert. |
2053 | AvmAssert((tag & 0x0f) >= 0 && (tag & 0x0f) < TRAIT_COUNT)do { } while (0); |
2054 | |
2055 | static const uint8_t kDesiredKind[TRAIT_COUNT] = |
2056 | { |
2057 | BKIND_NONE, // TRAIT_Slot |
2058 | BKIND_METHOD, // TRAIT_Method |
2059 | BKIND_GET, // TRAIT_Getter |
2060 | BKIND_SET, // TRAIT_Setter |
2061 | BKIND_NONE, // TRAIT_Class |
2062 | BKIND_NONE, // TRAIT_Function |
2063 | BKIND_NONE // TRAIT_Const |
2064 | }; |
2065 | |
2066 | const BindingKind desiredKind = BindingKind(kDesiredKind[kind]); |
2067 | const uint8_t dkMask = uint8_t(1 << desiredKind); |
2068 | |
2069 | // given baseBindingKind, what are legal desiredKinds? |
2070 | static const uint8_t kLegalBaseKinds[8] = |
2071 | { |
2072 | (1<<BKIND_METHOD) | (1<<BKIND_GET) | (1<<BKIND_SET), // BKIND_NONE |
2073 | (1<<BKIND_METHOD), // BKIND_METHOD |
2074 | 0, // BKIND_VAR |
2075 | 0, // BKIND_CONST |
2076 | 0, // unused |
2077 | (1<<BKIND_GET) | (1<<BKIND_SET), // BKIND_GET |
2078 | (1<<BKIND_GET) | (1<<BKIND_SET), // BKIND_SET |
2079 | (1<<BKIND_GET) | (1<<BKIND_SET) // BKIND_GETSET |
2080 | }; |
2081 | |
2082 | // given baseBindingKind, which desiredKinds *require* override? |
2083 | static const uint8_t kOverrideRequired[8] = |
2084 | { |
2085 | 0, // BKIND_NONE |
2086 | (1<<BKIND_METHOD), // BKIND_METHOD |
2087 | 0, // BKIND_VAR |
2088 | 0, // BKIND_CONST |
2089 | 0, // unused |
2090 | (1<<BKIND_GET), // BKIND_GET |
2091 | (1<<BKIND_SET), // BKIND_SET |
2092 | (1<<BKIND_GET) | (1<<BKIND_SET) // BKIND_GETSET |
2093 | }; |
2094 | |
2095 | if ((kLegalBaseKinds[baseBindingKind] & dkMask) == 0) |
2096 | goto failure; |
2097 | |
2098 | if (((kOverrideRequired[baseBindingKind] & dkMask) ? ATTR_override : 0) != (tag & ATTR_override)) |
2099 | goto failure; |
2100 | |
2101 | return baseBinding; |
2102 | |
2103 | failure: |
2104 | |
2105 | #ifdef AVMPLUS_VERBOSE |
2106 | if (pool->isVerbose(VB_traits) || pool->core->config.verifyonly) |
2107 | core->console << "illegal override in "<< this << ": " << Multiname(ns,name) <<"\n"; |
2108 | #endif |
2109 | if (toplevel) |
2110 | toplevel->throwVerifyError(kIllegalOverrideError, toplevel->core()->toErrorString(Multiname(ns,name)), toplevel->core()->toErrorString(this)); |
2111 | AvmAssert(!"unhandled verify error")do { } while (0); |
2112 | return BIND_NONE; |
2113 | } |
2114 | |
2115 | void Traits::ensureNonFinal(MethodInfo* minfo, const Toplevel* toplevel) const |
2116 | { |
2117 | if (!minfo->isFinal()) |
2118 | return; |
2119 | #ifdef AVMPLUS_VERBOSE |
2120 | if (pool->isVerbose(VB_traits)) |
2121 | core->console << "illegal override of final "<< minfo << " in " << this <<"\n"; |
2122 | #endif |
2123 | toplevel->throwVerifyError(kIllegalOverrideError, toplevel->core()->toErrorString(minfo), toplevel->core()->toErrorString(this)); |
2124 | AvmAssert(!"unhandled verify error")do { } while (0); |
2125 | } |
2126 | |
2127 | TraitsBindings* FASTCALL__attribute__((fastcall)) Traits::_getTraitsBindings() |
2128 | { |
2129 | AvmAssert(m_bindingsVerified)do { } while (0); |
2130 | TraitsBindings* tb = _buildTraitsBindings(/*toplevel*/NULL__null, /*includeTypes*/m_resolved); |
2131 | return tb; |
2132 | } |
2133 | |
2134 | TraitsMetadata* FASTCALL__attribute__((fastcall)) Traits::_getTraitsMetadata() |
2135 | { |
2136 | TraitsMetadata* tm = _buildTraitsMetadata(); |
2137 | return tm; |
2138 | } |
2139 | |
2140 | // Count supertypes in the given list, like strlen(). |
2141 | static uint32_t countSupertypes(Traits** list) |
2142 | { |
2143 | uint32_t n = 0; |
2144 | for (Traits** t = list; *t != NULL__null; t++) |
2145 | n++; |
2146 | return n; |
2147 | } |
2148 | |
2149 | // Initialize the m_primary_supertypes array by copying down the entries |
2150 | // from our base class (if it exists), and adding this traits if there is room. |
2151 | void Traits::build_primary_supertypes() |
2152 | { |
2153 | MMGC_STATIC_ASSERT(offsetof(Traits, m_primary_supertypes) + sizeof(m_primary_supertypes) < 256)typedef ::MMgc::static_assert_MMgc<sizeof (::MMgc::STATIC_ASSERTION_FAILED <(bool)(__builtin_offsetof(Traits, m_primary_supertypes) + sizeof(m_primary_supertypes) < 256)>)> MMgc_static_assert_line_2153; |
2154 | MMGC_STATIC_ASSERT(offsetof(Traits, m_supertype_cache) < 256)typedef ::MMgc::static_assert_MMgc<sizeof (::MMgc::STATIC_ASSERTION_FAILED <(bool)(__builtin_offsetof(Traits, m_supertype_cache) < 256)>)> MMgc_static_assert_line_2154; |
2155 | |
2156 | // compute m_supertype_offset and fill in m_primary_supertypes |
2157 | if (!base) { |
2158 | // class roots and interfaces |
2159 | m_supertype_offset = isInterface() ? offsetof(Traits, m_supertype_cache)__builtin_offsetof(Traits, m_supertype_cache) : offsetof(Traits, m_primary_supertypes)__builtin_offsetof(Traits, m_primary_supertypes); |
2160 | WB(core->gc, this, &m_primary_supertypes[0], this)core->gc->privateWriteBarrier(this, &m_primary_supertypes [0], (const void *) (this)); |
2161 | } else { |
2162 | // single inherited classes |
2163 | AvmAssert(!isInterface())do { } while (0); |
2164 | for (int i=0; i < MAX_PRIMARY_SUPERTYPE; i++) |
2165 | WB(core->gc, this, &m_primary_supertypes[i], base->m_primary_supertypes[i])core->gc->privateWriteBarrier(this, &m_primary_supertypes [i], (const void *) (base->m_primary_supertypes[i])); |
2166 | size_t off = base->m_supertype_offset; |
2167 | if (off != offsetof(Traits, m_supertype_cache)__builtin_offsetof(Traits, m_supertype_cache) && |
2168 | (off += sizeof(Traits*)) - offsetof(Traits, m_primary_supertypes)__builtin_offsetof(Traits, m_primary_supertypes) < sizeof(m_primary_supertypes)) { |
2169 | AvmAssert(off == (uint8_t) off)do { } while (0); |
2170 | m_supertype_offset = uint8_t(off); |
2171 | WB(core->gc, this, (Traits*)(uintptr_t(this)+off), this)core->gc->privateWriteBarrier(this, (Traits*)(uintptr_t (this)+off), (const void *) (this)); |
2172 | } else { |
2173 | // Inheritance is too deep to add this traits to m_primary_supertypes. |
2174 | // Make this traits "secondary", set m_supertype_offset to the cache. |
2175 | m_supertype_offset = offsetof(Traits, m_supertype_cache)__builtin_offsetof(Traits, m_supertype_cache); |
2176 | } |
2177 | } |
2178 | } |
2179 | |
2180 | // Initialize the m_secondary_supertypes array as follows: |
2181 | // * if this traits adds a new interface, create a new list with all |
2182 | // the entries from the base class, plus the base class itself, |
2183 | // plus the new interfaces |
2184 | // * if we do not add new interfaces and there's no base class, |
2185 | // use the empty list. |
2186 | // * if there is a base class, and the base class is primary, just |
2187 | // copy base->m_secondary_supertypes (containing any base interfaces) |
2188 | // * otherwise create a new secondary_supertypes list with everything |
2189 | // from the base class, plus the base class itself. Install this list |
2190 | // back in the base class so it can be shared by other leaf classes. |
2191 | // |
2192 | // allocSupertypeList returns un-scanned memory as we expect the |
2193 | // super types to be otherwise reachable to the GC, we also use |
2194 | // WB_SKIP to tell the GC we're skipping the WB on purpose. |
2195 | void Traits::build_secondary_supertypes() |
2196 | { |
2197 | MMgc::GC* gc = core->GetGC(); |
2198 | GCList<Traits> seen(gc, kListInitialCapacity); |
2199 | uint32_t count; |
2200 | if (!isInstanceType() || (count = countNewInterfaces(seen)) == 0) { |
2201 | // no new interfaces, attempt to share the base type's secondary list |
2202 | if (!base) { |
2203 | WB(gc, this, &this->m_secondary_supertypes, core->_emptySupertypeList)gc->privateWriteBarrier(this, &this->m_secondary_supertypes , (const void *) (core->_emptySupertypeList)); |
2204 | } else { |
2205 | Traits** base_list = base->m_secondary_supertypes; |
2206 | // If we require base in our secondary_supertypes list, so will other |
2207 | // sibling leaf types. Try to share the seconary_supertypes list by |
2208 | // inserting base at position 0. |
2209 | if (base->isPrimary() || base_list[0] == base) { |
2210 | // just copy the base list. |
2211 | WB(gc, this, &this->m_secondary_supertypes, base_list)gc->privateWriteBarrier(this, &this->m_secondary_supertypes , (const void *) (base_list)); |
2212 | } else { |
2213 | // must prepend base to base_list, save the copy on this type and base. |
2214 | count = countSupertypes(base_list); |
2215 | Traits** list = allocSupertypeList(gc, count + 1); |
2216 | WB_SKIP(gc, list, list, base)*(list) = base; |
2217 | for (uint32_t i=0; i < count; i++) |
2218 | WB_SKIP(gc, list, list+i+1, base_list[i])*(list+i+1) = base_list[i]; |
2219 | WB(gc, base, &base->m_secondary_supertypes, list)gc->privateWriteBarrier(base, &base->m_secondary_supertypes , (const void *) (list)); |
2220 | WB(gc, this, &this->m_secondary_supertypes, list)gc->privateWriteBarrier(this, &this->m_secondary_supertypes , (const void *) (list)); |
2221 | } |
2222 | } |
2223 | } else { |
2224 | // this type implements new interfaces so we need a new list |
2225 | this->m_implementsNewInterfaces = truetrue; |
2226 | if (base && !base->isPrimary() && base->m_secondary_supertypes[0] != base) { |
2227 | seen.add(base); |
2228 | count++; |
2229 | } |
2230 | Traits** list; |
2231 | uint32_t baseCount = base ? countSupertypes(base->m_secondary_supertypes) : 0; |
2232 | if (baseCount > 0) { |
2233 | uint32_t total = count + baseCount; |
2234 | list = allocSupertypeList(gc, total); |
2235 | for (Traits **d = list, **s = base->m_secondary_supertypes; *s != NULL__null; s++, d++) |
2236 | WB_SKIP(gc, list, d, *s)*(d) = *s; |
2237 | } else { |
2238 | list = allocSupertypeList(gc, count); |
2239 | } |
2240 | WB(gc, this, &this->m_secondary_supertypes, list)gc->privateWriteBarrier(this, &this->m_secondary_supertypes , (const void *) (list)); |
2241 | for (uint32_t i=0; i < count; i++) { |
2242 | WB_SKIP(gc, list, list+baseCount+i, seen[i])*(list+baseCount+i) = seen[i]; |
2243 | } |
2244 | } |
2245 | |
2246 | #ifdef DEBUG |
2247 | // sanity check to make sure we don't have any duplicate supertypes. |
2248 | GCList<Traits> supertypes(gc, kListInitialCapacity); |
2249 | for (int i = 0; i < MAX_PRIMARY_SUPERTYPE; i++) { |
2250 | Traits* t = m_primary_supertypes[i]; |
2251 | if (t != NULL__null) { |
2252 | if (supertypes.indexOf(t) != -1) { |
2253 | core->console << "t " << this << " dup primary " << t << "\n"; |
2254 | AvmAssert(false)do { } while (0); |
2255 | } |
2256 | supertypes.add(t); |
2257 | } |
2258 | } |
2259 | for (Traits** st = m_secondary_supertypes; *st != NULL__null; st++) { |
2260 | Traits* t = *st; |
2261 | if (supertypes.indexOf(t) != -1) { |
2262 | core->console << "t " << this << " dup secondary " << t << "\n"; |
2263 | AvmAssert(false)do { } while (0); |
2264 | } |
2265 | supertypes.add(t); |
2266 | } |
2267 | #endif |
2268 | } |
2269 | |
2270 | // search interfaces and bases that didn't fit in m_primary_supertypes, |
2271 | // and cache positive/negative results |
2272 | boolbool Traits::secondary_subtypeof(Traits* t) |
2273 | { |
2274 | for (Traits** s = m_secondary_supertypes; *s != NULL__null; s++) { |
2275 | if (t == *s) { |
2276 | m_supertype_cache = t; |
2277 | return truetrue; |
2278 | } |
2279 | } |
2280 | m_supertype_neg_cache = t; |
2281 | return falsefalse; |
2282 | } |
2283 | |
2284 | // create a new supertype list of the given length |
2285 | Traits** Traits::allocSupertypeList(GC* gc, uint32_t size) |
2286 | { |
2287 | // kContainsPointer left off on purpose, see comments in header. |
2288 | return (Traits**) gc->Alloc((size+1) * sizeof(Traits*), MMgc::GC::kZero); |
2289 | } |
2290 | |
2291 | // Returns true if a value of type rhs can be assigned to a variable |
2292 | // or parameter of type lhs without modifying the representation of the value. |
2293 | // It is not just a subtypeof test, for example int is a subtype of Object |
2294 | // but they use different representations. |
2295 | boolbool Traits::canAssign(Traits* lhs, Traits* rhs) |
2296 | { |
2297 | if (!Traits::isMachineCompatible(lhs,rhs)) { |
2298 | // no machine type is assignment-compatible with any other |
2299 | return falsefalse; |
2300 | } |
2301 | |
2302 | if (!lhs) |
2303 | return truetrue; |
2304 | |
2305 | // type on right must be same class or subclass of type on left. |
2306 | Traits* t = rhs; |
2307 | while (t != lhs && t != NULL__null) |
2308 | t = t->base; |
2309 | return t != NULL__null; |
2310 | } |
2311 | |
2312 | // Return the slot type (if slot), return type (if getter), or NULL. |
2313 | Traits* Traits::readBinding(Traits* traits, Binding b) |
2314 | { |
2315 | AvmAssert((!traits && AvmCore::bindingKind(b) == BKIND_NONE) ||do { } while (0) |
2316 | (traits && traits->isResolved()))do { } while (0); |
2317 | switch (AvmCore::bindingKind(b)) |
2318 | { |
2319 | default: |
2320 | AvmAssert(false)do { } while (0); // internal error - illegal binding type |
2321 | case BKIND_GET: |
2322 | case BKIND_GETSET: |
2323 | { |
2324 | int m = AvmCore::bindingToGetterId(b); |
2325 | MethodInfo *f = traits->getTraitsBindings()->getMethod(m); |
2326 | MethodSignaturep fms = f->getMethodSignature(); |
2327 | return fms->returnTraits(); |
2328 | } |
2329 | case BKIND_SET: |
2330 | // TODO lookup type here. get/set must have same type. |
2331 | case BKIND_NONE: |
2332 | // dont know what this is |
2333 | // fall through |
2334 | case BKIND_METHOD: |
2335 | // extracted method or dynamic data, don't know which |
2336 | return NULL__null; |
2337 | case BKIND_VAR: |
2338 | case BKIND_CONST: |
2339 | return traits->getTraitsBindings()->getSlotTraits(AvmCore::bindingToSlotId(b)); |
2340 | } |
2341 | } |
2342 | |
2343 | } |