@@ -3301,20 +3301,31 @@ class Interpreter {
33013301 } break ;
33023302 case Value::OBJECT:
33033303 const auto obj = static_cast <HeapObject *>(scratch.v .h );
3304- // TODO: Do this by constructing a FRAME_INVARIANT here?
3305- runInvariants (f.location , obj);
3304+ const auto loc = f.location ;
3305+
3306+ f.kind = FRAME_OBJECT_TO_JSON;
3307+ f.first = true ;
3308+ f.val = scratch;
3309+ f.str .clear ();
33063310 std::map<UString, const Identifier *> fields;
33073311 for (const auto &field : objectFields (obj, true )) {
33083312 fields[field->name ] = field;
33093313 }
3310- if (fields.empty ()) {
3314+ std::swap (f.manifestFields , fields); // Swap instead of deep copy.
3315+
3316+ // runInvariants re-enters evaluate() so it messes with the stack.
3317+ // Hence we need to make sure that the FRAME_OBJECT_TO_JSON is set up _first_,
3318+ // even if the object is "empty" (no fields to manifest).
3319+ // TODO: Do this by constructing a FRAME_INVARIANT here?
3320+ runInvariants (loc, obj);
3321+
3322+ // fields was already cleared above, and `f` may have been invalidated
3323+ // by the stack-manipulation inside runInvariants. So we need to explicitly
3324+ // look at stack.top().manifestFields.
3325+ if (stack.top ().manifestFields .empty ()) {
33113326 scratch = makeString (U" { }" );
33123327 } else {
3313- f.kind = FRAME_OBJECT_TO_JSON;
3314- f.first = true ;
3315- f.val = scratch;
3316- f.str .clear ();
3317- std::swap (f.manifestFields , fields); // Swap instead of deep copy.
3328+ assert (stack.top ().kind == FRAME_OBJECT_TO_JSON);
33183329 goto replaceframe;
33193330 }
33203331 break ;
0 commit comments