facebook / hhvm

A virtual machine for executing programs written in Hack.
https://hhvm.com
Other
18.13k stars 2.98k forks source link

HPHP::Class::setInterfaceVtables(): assertion `vtableVec[slot].vtable == nullptr' failed. #7050

Open firodj opened 8 years ago

firodj commented 8 years ago

HHVM Version

HipHop VM 3.13.1 (dbg) Compiler: tags/HHVM-3.13.1-0-g4f382ad928a6e2a0607a8dcb251002aca77f11f6 Repo schema: 655b5912cb8136e9df6f9be972153e38ac446e0f

DISTRIB_ID=Ubuntu DISTRIB_RELEASE=14.04 DISTRIB_CODENAME=trusty DISTRIB_DESCRIPTION="Ubuntu 14.04.4 LTS"

Standalone code, or other way to reproduce the problem

Let say I have a Laravel 5.1 project, and it compile sucessfully (after removing class_alias, extract, compact, and get_defined_vars).

$ hhvm --hphp -t hhbc -v AllVolatile=true -v WholeProgram=true --input-list compile.list --output-dir repo-bytecode/ -k1 -l4

The portion of the class:

<?php
namespace App\Models;

use Eloquent;

use App\Interfaces\AntarMuka;

class BaseModel extends Eloquent implements AntarMuka\Iface7, AntarMuka\Iface9 {
...

Expected result

No assertion failure.

Actual result

Assertion failure: /tmp/tmp.OwZJHUMLSO/hphp/runtime/vm/class.cpp:2496: void HPHP::Class::setInterfaceVtables(): assertion `vtableVec[slot].vtable == nullptr' failed.

I ran server with these options:

$  sudo env TRACE=class_load:3 hhvm -m server -c /etc/hhvm/php.ini -c /etc/hhvm/server.ini -u www-data

with a portion of server.ini:

hhvm.repo.central.path = /var/run/hhvm/precompiled/hhvm.hhbc
hhvm.repo.authoritative = true
hhvm.server.source_root=/laravel_vagrant/

Trace result

...
Setting interface vtables for class App\Models\BaseModel. 9 interfaces, 9 vtable slots, 11 total methods
  ArrayAccess @ slot 1
    offsetExists:0x7f6ed7423e80 @ slot 0
    offsetGet:0x7f6ed7423f20 @ slot 1
    offsetSet:0x7f6ed7423fc0 @ slot 2
    offsetUnset:0x7f6ed7424060 @ slot 3
  Illuminate\Contracts\Support\Arrayable @ slot 7
    toArray:0x7f6ed7421a20 @ slot 0
  Illuminate\Contracts\Support\Jsonable @ slot 6
    toJson:0x7f6ed74218e0 @ slot 0
  JsonSerializable @ slot 5
    jsonSerialize:0x7f6ed7421980 @ slot 0
  Illuminate\Contracts\Queue\QueueableEntity @ slot 0
    getQueueableId:0x7f6ed7420240 @ slot 0
  Illuminate\Contracts\Routing\UrlRoutable @ slot 3
    getRouteKey:0x7f6ed74204c0 @ slot 0
    getRouteKeyName:0x7f6ed7420560 @ slot 1
  Stringish @ slot 2
    __toString:0x7f6ed7424380 @ slot 0
  App\Publishing\Lib\AntarMuka\Iface7 @ slot 8
  App\Publishing\Lib\AntarMuka\Iface9 @ slot 7

the App\Publishing\Lib\AntarMuka\Iface7 and App\Publishing\Lib\AntarMuka\Iface9 is interface without methods (dummy).

As you can see, the HPHP::Class::setInterfaceVtables() calculate vtables into 9 because it sees the max slot is 8 by App\Publishing\Lib\AntarMuka\Iface7. But the conflict is on slot 7 which have already given to Illuminate\Contracts\Support\Arrayable. And who are using slot 4???

How the hhbc emitter calculate the slot for interfaces? I have digging on source but couldnt find the answer. I have suspected of how the code work in compute_iface_vtables on hhbbc/index.cpp.

https://github.com/facebook/hhvm/commit/8148d86cb082150a093b7de90e668a5a624e91e3

Orvid commented 8 years ago

If I understand the implementation correctly, there is nothing in slot 4 because the class doesn't implement any interfaces that can be assigned to slot 4. HHBBC generates a "compact" interface table, where-by multiple interfaces can share the same slot, but only if no class implements both interfaces.

The conflict in slot 7 is likely the issue, as no two interfaces on the same class should be in the same slot.

Orvid commented 8 years ago

I can't seem to reproduce this. Do you have more info on the heirarchies and how each interface and base class is defined?

firodj commented 8 years ago

@Orvid I will give another cases with exactly same problem which the HH\IMemoizeParam have collision with other interface slot on class App\Models\Article, but now we can see the CollisionGraph

<?hh

class_alias('Illuminate\Database\Eloquent\Model',   'Eloquent');
<?hh
namespace App\Models;

class Article extends BaseModel implements \HH\IMemoizeParam {
...
}
<?hh
namespace App\Models;

class BaseModel extends Eloquent {
...
}
<?php

namespace Illuminate\Database\Eloquent;

use ArrayAccess;
use JsonSerializable;
use Illuminate\Contracts\Support\Jsonable;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Contracts\Routing\UrlRoutable;
use Illuminate\Contracts\Queue\QueueableEntity;

abstract class Model implements ArrayAccess, Arrayable, Jsonable, JsonSerializable, QueueableEntity, UrlRoutable
{
...
}

Then I enable the trace with:

$ TRACE=hhbbc_iface:3 hhvm --hphp -t hhbc -v AllVolatile=true -v WholeProgram=true --input-list compile.list --output-dir repo-bytecode/ -k1 -l4

After then the trace result in /tmp/hphp.log:

$ cat /tmp/hphp.log | grep IMemoizeParam
HH\IMemoizeParam   0  0 []

I move the IMemoizeParam to BaseModels still the same, I remove from Article still alose the same.

But, when I move the IMemoizeParam into Eloquent this time different:

...
abstract class Model implements ArrayAccess, Arrayable, Jsonable, JsonSerializable, QueueableEntity, UrlRoutable, \HH\IMemoizeParam
{
...
}
$ cat /tmp/hphp.log | grep IMemoizeParam
HH\IMemoizeParam   7  2 [Illuminate\Contracts\Routing\UrlRoutable, Illuminate\Contracts\Queue\QueueableEntity, Illuminate\Contracts\Support\Jsonable, Stringish, Illuminate\Contracts\Support\Arrayable, JsonSerializable, ArrayAccess]

And I still curious about how class_alias so I change the BaseModel to use the true class name of the parent which are:

...
class BaseModel extends \Illuminate\Database\Eloquent\Model {
...
}

And then the result is become correct!

$ cat /tmp/hphp.log | grep IMemoizeParam
HH\IMemoizeParam   8  8 [Illuminate\Contracts\Routing\UrlRoutable, Illuminate\Contracts\Queue\QueueableEntity, Illuminate\Contracts\Support\Jsonable, Stringish, Illuminate\Contracts\Support\Arrayable, JsonSerializable, ArrayAccess, Codesleeve\Stapler\ORM\StaplerableInterface]

I will try to check another class that extends from parent using class_alias, maybe the Hhbc misses these aliases?

firodj commented 7 years ago

is this issue already fixed or not ? the problem is with the class_alias. The parent class that being aliased is not resolved when the child class building the vtables.

Orvid commented 7 years ago

I'm not entirely sure if it was fixed or not, I don't think I was ever able to reproduce it.