diff --git a/internal/bytecode/bytecode.go b/internal/bytecode/bytecode.go index 27403a75..06eb9943 100644 --- a/internal/bytecode/bytecode.go +++ b/internal/bytecode/bytecode.go @@ -121,6 +121,12 @@ const ( OpDefinedConstTop // A = Names index; "constant" if the top-level `::Name` exists, else nil OpRegexpOnce // A = target pc past the interpolation build; guards a /o literal: if the occurrence's Regexp is already memoised, push it and jump to A, else fall through to (re)build once OpDefinedSuper // "super" if the method `super` would reach from the current frame exists, else nil; never evaluates the super arguments + OpConcatStrings // A = part count; pops A values and concatenates their string forms (MRI's concatstrings). It does NOT dispatch String#+, which is what makes an interpolated literal independent of a redefined operator + OpObjToString // pops a value; pushes it unchanged when it is a String (subclasses included), else its #to_s -- or MRI's rb_any_to_s of the value when that #to_s did not answer with a String. MRI's objtostring + anytostring in one + + // OpLast is one past the highest opcode. It is not an instruction; it sizes + // per-opcode tables (see the VM's basic-operator cache). Keep it last. + OpLast ) // Defined tag Strings. @@ -176,6 +182,7 @@ var opNames = map[Op]string{ OpSplatToArray: "splat_to_array", OpConcatArray: "concat_array", OpSendArray: "send_array", OpKwGiven: "kw_given", OpHashSetPair: "hash_set_pair", OpHashMerge: "hash_merge", OpSendBlockArg: "send_block_arg", OpSendArrayBlockArg: "send_array_block_arg", + OpConcatStrings: "concat_strings", OpObjToString: "obj_to_string", OpRegexp: "regexp", OpRegexpDyn: "regexp_dyn", OpRegexpOnce: "regexp_once", OpTruthy: "truthy", OpRaiseNoMatch: "raise_no_match", OpBinding: "binding", OpDefineClassScoped: "define_class_scoped", diff --git a/internal/compiler/compiler.go b/internal/compiler/compiler.go index b4c2caca..34241cb1 100644 --- a/internal/compiler/compiler.go +++ b/internal/compiler/compiler.go @@ -713,13 +713,35 @@ func (c *Compiler) compileNode1(n ast.Node) { case *ast.StringLit: b.emit(bytecode.OpPushConst, b.addConst(c.newStrLit(v.Value)), 0) case *ast.StrInterp: - // Concatenate each part coerced with to_s onto a growing string. - b.emit(bytecode.OpPushConst, b.addConst(object.NewString("")), 0) + // MRI's concatstrings: push each part, then join them in one step. This + // used to push "" and fold the parts on with OpAdd, which made an + // interpolated literal depend on String#+ -- and, now that the operator + // opcodes honour a redefinition, `class String; def +(o); :X; end; end` + // broke every "#{}" in the program. MRI never dispatches #+ here, and + // neither does this. + // + // A literal segment is also pushed AS IS. The old shape sent #to_s to + // every part including the literal ones, so a redefined String#to_s broke + // interpolation too (measured on main, before any of this: ruby answers + // "A1B", rbgo raised TypeError). MRI emits its tostring only for the + // embedded expressions, which is what the type switch below reproduces. for _, part := range v.Parts { c.compileNode(part) - b.emit(bytecode.OpSend, b.addName("to_s"), 0) - b.emit(bytecode.OpAdd, 0, 0) + if _, literal := part.(*ast.StringLit); !literal { + // MRI's objtostring (+ its anytostring fallback), as one opcode. + // It is NOT a plain `send :to_s`: a value that is already a String + // is left alone, subclasses included, so a redefined String#to_s + // does not change what an interpolation produces -- + // + // class S < String; def to_s; "SUB"; end; end + // "#{S.new("x")}" ruby 4.0.5 "x" a bare send "SUB" + // + // -- and when #to_s answers with something other than a String the + // fallback renders the VALUE, not that answer. + b.emit(bytecode.OpObjToString, 0, 0) + } } + b.emit(bytecode.OpConcatStrings, len(v.Parts), 0) case *ast.SymbolLit: // Intern the box so every `:name` literal across the program shares one // Value: :foo.equal?(:foo) then holds by pointer, and the const pool holds diff --git a/internal/vm/arith.go b/internal/vm/arith.go index 0959e697..476cec1f 100644 --- a/internal/vm/arith.go +++ b/internal/vm/arith.go @@ -251,7 +251,27 @@ func (vm *VM) hasCustomNeq(a object.Value) bool { return vm.overriddenNegation(a, "!=") != nil } +// binaryOp is an operator opcode's evaluation: MRI's vm_opt_plus and siblings. +// It dispatches a redefined operator and otherwise keeps the inline path, which +// is the BASIC_OP_UNREDEFINED_P guard rbgo was missing -- see +// basic_op_redefinition.go for what that cost. func (vm *VM) binaryOp(op bytecode.Op, a, b object.Value) object.Value { + if m := vm.overriddenBasicOp(op, a); m != nil { + if m == basicOpRemoved { + // The method was removed; raise the NoMethodError a send would. + return raise("NoMethodError", "undefined method '%s' for %s", + basicOpName(op), vm.undefinedMethodReceiver(a)) + } + return vm.invoke(m, a, []object.Value{b}, nil) + } + return vm.binaryOpBuiltin(op, a, b) +} + +// binaryOpBuiltin is the inline arithmetic itself, with no redefinition check. +// The operator methods installed by defineBasicOperatorMethods call it too, so +// `1 + 2` and `1.send(:+, 2)` run one implementation rather than two that could +// disagree -- which is how the guard's absence stayed invisible. +func (vm *VM) binaryOpBuiltin(op bytecode.Op, a, b object.Value) object.Value { // An instance of a user subclass of a built-in value type uses that value's // own operators (so a String-subclass "+", an Array-subclass "*", and the // comparisons all work), on either side of the operator. diff --git a/internal/vm/basic_op_redefinition.go b/internal/vm/basic_op_redefinition.go new file mode 100644 index 00000000..2c2ccc67 --- /dev/null +++ b/internal/vm/basic_op_redefinition.go @@ -0,0 +1,439 @@ +// Copyright (c) the go-embedded-ruby/ruby authors +// +// SPDX-License-Identifier: BSD-3-Clause + +package vm + +import ( + "strconv" + "strings" + "sync/atomic" + + "github.com/go-embedded-ruby/ruby/internal/bytecode" + "github.com/go-embedded-ruby/ruby/internal/object" +) + +// The operator opcodes (OpAdd … OpNeq) compute their result inline for the +// built-in value types, which is what makes arithmetic fast. MRI does the same +// -- vm_opt_plus and friends -- but every one of those specialisations is +// guarded by BASIC_OP_UNREDEFINED_P: the moment anything redefines the +// operator for that class, the opcode stops specialising and dispatches the +// method instead (vm_insnhelper.c, vm.c's vm_init_redefined_flag). +// +// rbgo had the specialisation and not the guard, so a redefinition was honoured +// through #send and ignored by the operator, in the same run: +// +// class Integer; def +(o); :HIJACKED; end; end +// 1 + 2 ruby 4.0.5 :HIJACKED before 3 +// 1.send(:+, 2) ruby 4.0.5 :HIJACKED before :HIJACKED +// +// Measured over the 88 (class, operator) pairs the opcodes can reach, 60 +// diverged that way, and in all 60 #send already answered correctly -- the +// method was installed and reachable, and only the opcode could not see it. +// The one operator that did work was #!=, through the explicit hasCustomNeq +// check: the guard was known, and applied to exactly one of them. +// +// The same blindness covered subclasses and prepends, which is why the guard +// resolves on the ORIGINAL receiver rather than on the unwrapped value: +// +// class S < String; def +(o); :SUB; end; end +// S.new("a") + "b" ruby :SUB before "ab" +// module M; def +(o); :MOD; end; end +// class U < String; prepend M; end +// U.new("a") + "b" ruby :MOD before "ab" +// +// basicOpSnapshot records, once at the end of bootstrap, the *Method each +// (basic class, operator) pair resolves to while nothing has touched it. After +// that, "redefined" is "resolves to something else", which covers a plain +// redefinition, a subclass, a prepended module, an alias, define_method and an +// undef with one test -- and needs no hook in any of their paths. +type basicOpCacheEntry struct { + serial uint64 + cls *RClass + m *Method +} + +type basicOpSnapshot struct { + taken bool + // cache is indexed by opcode; see overriddenBasicOp. + cache [bytecode.OpLast]basicOpCacheEntry + // m[class][op] is the method the pair resolved to at bootstrap. A pair MRI + // does not define at all (String#-, Hash#+) is absent, so anything that + // resolves later for it is a user's. + m map[*RClass]map[bytecode.Op]*Method +} + +// basicOpClasses are the classes whose operators the opcodes specialise. It is +// rbgo's analogue of MRI's vm_init_redefined_flag list, restricted to the types +// binaryOpBuiltin actually handles inline -- a type it already dispatches needs +// no guard. +func (vm *VM) basicOpClasses() []*RClass { + // A nil is filtered HERE rather than guarded at each use. The fields are all + // set by the time the snapshot is taken, so a nil would mean a registration + // was skipped -- a build-tagged one, say -- and the honest handling is to + // leave that class unwatched, which keeps its operators on the inline path. + all := []*RClass{ + vm.cInteger, vm.cFloat, vm.cString, vm.cArray, vm.cHash, + vm.cSymbol, vm.cNilClass, vm.cTrueClass, vm.cFalseClass, + vm.cRational, vm.cComplex, vm.cRange, vm.cTime, + } + out := all[:0] + for _, c := range all { + if c != nil { + out = append(out, c) + } + } + return out +} + +// basicOps are the operators those opcodes carry. +var basicOps = []bytecode.Op{ + bytecode.OpAdd, bytecode.OpSub, bytecode.OpMul, bytecode.OpDiv, + bytecode.OpMod, bytecode.OpLt, bytecode.OpGt, bytecode.OpLe, + bytecode.OpGe, bytecode.OpEq, bytecode.OpNeq, +} + +// basicOpName is the method name behind an operator opcode. +// +// It is written out rather than delegated to arithOpName and compareOpName, +// which each answer for their own group and have a default arm for it: +// arithOpName returns "%" for anything that is not one of the four arithmetic +// opcodes, as its comment says it may ("only the five arithmetic opcodes reach +// binaryOp's default branch"). Building on that gave every comparison opcode +// the name "%", so the guard compared the wrong method and the four ordering +// operators silently kept specialising -- measured, before this was written out. +func basicOpName(op bytecode.Op) string { + switch op { + case bytecode.OpAdd: + return "+" + case bytecode.OpSub: + return "-" + case bytecode.OpMul: + return "*" + case bytecode.OpDiv: + return "/" + case bytecode.OpMod: + return "%" + case bytecode.OpLt: + return "<" + case bytecode.OpGt: + return ">" + case bytecode.OpLe: + return "<=" + case bytecode.OpGe: + return ">=" + case bytecode.OpEq: + return "==" + case bytecode.OpNeq: + return "!=" + } + return "" +} + +// snapshotBasicOperators runs at the end of bootstrap, when every built-in is +// registered and no user code has run. Whatever resolves now IS the built-in. +func (vm *VM) snapshotBasicOperators() { + snap := &basicOpSnapshot{taken: true, m: map[*RClass]map[bytecode.Op]*Method{}} + for _, cls := range vm.basicOpClasses() { + per := map[bytecode.Op]*Method{} + for _, op := range basicOps { + // No name check: basicOpName answers for every member of basicOps, and + // TestBasicOpNameRefusesANonOperator asserts exactly that, so a guard + // here would be unreachable code the coverage gate then reports. + if m := undefAsNil(lookupMethod(cls, basicOpName(op))); m != nil { + per[op] = m + } + } + snap.m[cls] = per + } + vm.basicOps = snap +} + +// overriddenBasicOp reports the method an operator should dispatch instead of +// specialising, or nil to keep the inline path. It is the analogue of +// BASIC_OP_UNREDEFINED_P, reached through the same full dispatch chain +// findMethod walks (singleton class, extended and prepended modules, then the +// ancestry) so a `def o.+`, an `o.extend M` and a subclass are all seen. +// +// It costs one method resolution per operator evaluation on a receiver whose +// class is in the snapshot. That is the price of the answer being right; the +// inline caches the sends use are not available here because an opcode carries +// no call-site data in rbgo. A redefinition is rare, so the common result is +// "same pointer as at bootstrap" and the inline path is kept. +func (vm *VM) overriddenBasicOp(op bytecode.Op, recv object.Value) *Method { + if vm.basicOps == nil || !vm.basicOps.taken { + // Before the snapshot (during bootstrap, and in the AOT runtime before + // setup finishes) nothing can have been redefined yet. + return nil + } + cls := vm.classOf(recv) + // One cache entry per operator, carrying the serial it was computed at and + // the receiver class it was computed for -- the same shape as the send sites' + // inlineCache, and for the same reason. An arithmetic loop is monomorphic in + // its receiver class, so the steady state is a serial compare, a pointer + // compare and an array load, with no method resolution at all. That matters: + // the guard sits on every operator evaluation, so paying a findMethod per + // iteration would be paying for an answer that cannot have changed. + // + // globalMethodSerial moves only when a method table does, which after + // bootstrap is rare, so an entry survives for the life of the loop. + if e := &vm.basicOps.cache[op]; e.serial == globalMethodSerial.Load() && e.cls == cls { + return e.m + } + basicOpSlowPath.Add(1) + m := vm.resolveBasicOpOverride(op, recv, cls) + vm.basicOps.cache[op] = basicOpCacheEntry{serial: globalMethodSerial.Load(), cls: cls, m: m} + return m +} + +// resolveBasicOpOverride is overriddenBasicOp's slow path: the actual lookup, +// run on a cache miss. +func (vm *VM) resolveBasicOpOverride(op bytecode.Op, recv object.Value, cls *RClass) *Method { + name := basicOpName(op) + if name == "" { + return nil + } + per, watched := vm.basicOps.m[cls] + if !watched { + // Not a specialised class. Either binaryOpBuiltin dispatches it anyway, + // or it is a subclass of one -- and a subclass is exactly the case the + // snapshot cannot answer by identity, so resolve and compare against the + // ancestor's record. + if base := vm.snapshotAncestor(cls); base != nil { + per = vm.basicOps.m[base] + } else { + return nil + } + } + m := vm.findMethod(recv, name) + if m == per[op] { + return nil + } + if m == nil { + // per[op] is necessarily non-nil here, so this IS a removal: the compare + // above returned for m == per[op], and m is nil, so per[op] cannot be. + // The arm that used to test for it with `if _, had := per[op]; had` -- + // and the `return nil` under it for "no record before, none now" -- + // could not be reached. The coverage ratchet is what found that: the + // function sat at 93.8% with exactly one unreachable block, and the + // case the dead arm was written for (Hash#+, which MRI does not define + // either) is already answered by the m == per[op] compare, where both + // sides are nil. wave57_guard_internals_test.go asserts that Hash#+ + // resolves to nil, and it still does -- by the earlier return. + // + // The built-in record was REMOVED (remove_method :+ / undef_method). + // MRI raises there, because the opcode's specialisation is gone along + // with the method: + // + // class Integer; remove_method :+; end + // 1 + 2 ruby 4.0.5 NoMethodError: undefined method '+' for an + // instance of Integer + // + // Falling back to the inline path would make the removal silently do + // nothing -- and installing the records is what makes remove_method + // SUCCEED here at all, so without this the new records would have + // turned a NameError into a no-op. + return basicOpRemoved + } + return m +} + +// basicOpSlowPath counts cache MISSES in overriddenBasicOp -- the only path +// that resolves a method. It exists so the cache's effect can be asserted by a +// COUNT rather than by a clock: the claim is "no method resolution per +// iteration", and a count settles that on a loaded machine where a timing +// cannot. See TestOperatorGuardCostsNoResolutionPerIteration. +var basicOpSlowPath atomic.Uint64 + +// basicOpRemoved is the sentinel overriddenBasicOp returns when the built-in +// operator method has been removed: there is nothing to invoke, and the opcode +// must raise rather than specialise. +var basicOpRemoved = &Method{name: ""} + +// snapshotAncestor finds the nearest ancestor of cls that the snapshot covers, +// so a user subclass of String is compared against String's own operator +// records rather than treated as unwatched. +func (vm *VM) snapshotAncestor(cls *RClass) *RClass { + for c := cls; c != nil; c = c.super { + if _, ok := vm.basicOps.m[c]; ok { + return c + } + } + return nil +} + +// defineBasicOperatorMethods installs the operator methods MRI defines and rbgo +// only ever computed in the opcode. Measured against ruby 4.0.5, fourteen were +// missing entirely: +// +// Integer + - * / Float + - * / +// String + * % Array + - * +// +// Integer.instance_method(:+) ruby 4.0.5 # +// before NameError +// +// Each body is the opcode's own path (binaryOpBuiltin), so there is one +// implementation of the arithmetic and not two that could drift. MRI declares +// all of them with argc 1 (rb_define_method(rb_cInteger, "+", rb_int_plus, 1) +// and its siblings, numeric.c ruby_4_0:6342-6345, 6482-6485), which is what +// #arity and #parameters report. +// +// Installing them is also what lets the redefinition guard be exact: with a +// record present, "redefined" means "resolves to a DIFFERENT record", so a +// subclass that does not override keeps the inline path. +func (vm *VM) defineBasicOperatorMethods() { + arith := []bytecode.Op{bytecode.OpAdd, bytecode.OpSub, bytecode.OpMul, bytecode.OpDiv} + for _, op := range arith { + installOperatorMethod(vm.cInteger, op) + installOperatorMethod(vm.cFloat, op) + } + for _, op := range []bytecode.Op{bytecode.OpAdd, bytecode.OpMul, bytecode.OpMod} { + installOperatorMethod(vm.cString, op) + } + for _, op := range []bytecode.Op{bytecode.OpAdd, bytecode.OpSub, bytecode.OpMul} { + installOperatorMethod(vm.cArray, op) + } +} + +// installOperatorMethod gives cls the operator method for op, unless it already +// has one -- a registration site elsewhere wins, and this must never replace it. +// +// It takes the OPCODE rather than the name: looking the opcode up from a name +// could fail, and that arm was unreachable for the five names this passes, so +// the gate reported it as dead. Passing the opcode removes the arm instead of +// testing an impossibility. +func installOperatorMethod(cls *RClass, op bytecode.Op) { + name := basicOpName(op) + if _, exists := cls.methods[name]; exists { + return + } + cls.defineArgc(name, 1, func(vm *VM, self object.Value, args []object.Value, _ *Proc) object.Value { + return vm.binaryOpBuiltin(op, self, args[0]) + }) +} + +// concatStringParts is MRI's concatstrings: it joins already-coerced +// interpolation parts into one fresh, unfrozen String. +// +// A part that is not a String is MRI's anytostring arm. The compiler has +// already sent #to_s to every embedded expression, so reaching here with a +// non-String means that #to_s returned one, and MRI then falls back to +// rb_any_to_s rather than failing: +// +// class Foo; def to_s; :notastring; end; end +// "x#{Foo.new}y" ruby 4.0.5 "x#y" +// before TypeError: no implicit conversion of Symbol +// +// That is the same rb_any_to_s shape Kernel#to_s owes (#756) but a separate +// path: this one is the interpolation coercion, and it does not consult the +// #to_s method again -- MRI does not either, or a #to_s returning a non-String +// would recurse. +func (vm *VM) concatStringParts(parts []object.Value) object.Value { + var b strings.Builder + enc := "" + for _, p := range parts { + // Every part is a String by construction: a literal segment is one, and + // an expression segment has been through OpObjToString, which returns a + // String or the rb_any_to_s rendering of the value. A defensive arm here + // was unreachable, and the coverage gate said so. + s := p.(*object.String) + if e := s.Enc; e != "" && e != "US-ASCII" && enc == "" { + enc = e + } + b.Write(s.Bytes()) + } + if enc != "" { + return object.NewStringBytesEnc([]byte(b.String()), enc) + } + return object.NewString(b.String()) +} + +// anyToSForConcat renders MRI's rb_any_to_s: `#`, the form a +// value gets when it has no usable #to_s. +func (vm *VM) anyToSForConcat(v object.Value) string { + return "#<" + vm.classOf(v).name + ":0x" + hex16ForConcat(uint64(vm.refID(v))) + ">" +} + +func hex16ForConcat(v uint64) string { + h := strconv.FormatUint(v, 16) + for i := len(h); i < 16; i++ { + h = "0" + h + } + return h +} + +// basicOpWasDefined reports whether this operator HAD a real method for this +// receiver's class at snapshot time. When it did, "no method resolves now" +// means it was removed, and the send path must raise NoMethodError rather than +// fall back to the opcode -- that fallback exists for the operators which never +// had a record, and reaching it after a remove_method made the removal a no-op. +func (vm *VM) basicOpWasDefined(op bytecode.Op, recv object.Value) bool { + if vm.basicOps == nil || !vm.basicOps.taken { + return false + } + cls := vm.classOf(recv) + per, ok := vm.basicOps.m[cls] + if !ok { + if base := vm.snapshotAncestor(cls); base != nil { + per = vm.basicOps.m[base] + } else { + return false + } + } + _, had := per[op] + return had +} + +// objToString is MRI's objtostring instruction plus its anytostring fallback: +// +// if (RB_TYPE_P(recv, T_STRING)) return recv; +// str = rb_funcall(recv, idTo_s, 0); +// return rb_obj_as_string_result(str, recv); +// +// The T_STRING arm matters and is measured: a String is returned UNTOUCHED, +// subclasses included, so a redefined String#to_s does not change what an +// interpolation produces -- +// +// class S < String; def to_s; "SUB"; end; end +// "#{S.new("x")}" ruby 4.0.5 "x" +// S.new("x").to_s ruby 4.0.5 "SUB" +// +// -- which a plain `send :to_s` gets wrong in both directions at once. +// objToStringWith is objToString with the refinement the CALL SITE resolved, if +// any. The opcode passes it because only the interpreter loop knows the lexical +// scope a `using` activated refinements in; refined is nil everywhere else, and +// then this is a plain dispatch. +func (vm *VM) objToStringWith(v object.Value, refined *Method) object.Value { + if s := stringTypeOf(v); s != nil { + return s + } + var r object.Value + if refined != nil { + r = vm.invoke(refined, v, nil, nil) + } else { + r = vm.send(v, "to_s", nil, nil) + } + if s, ok := r.(*object.String); ok { + return s + } + return object.NewString(vm.anyToSForConcat(v)) +} + +// stringTypeOf returns the String a value IS -- itself, or the built-in value a +// String subclass instance wraps -- or nil when it is not a String at all. It +// is rbgo's RB_TYPE_P(v, T_STRING): a subclass instance is an *RObject carrying +// the String in its builtin slot, so a bare type assertion misses it. +// +// Not to be confused with regexp.go's stringLike, which answers a different +// question: "String or Symbol, as text", for a regexp group key. +func stringTypeOf(v object.Value) *object.String { + if s, ok := v.(*object.String); ok { + return s + } + if o, ok := v.(*RObject); ok { + if s, ok := o.builtin.(*object.String); ok { + return s + } + } + return nil +} diff --git a/internal/vm/builtins.go b/internal/vm/builtins.go index 6a17ba39..10244645 100644 --- a/internal/vm/builtins.go +++ b/internal/vm/builtins.go @@ -6550,6 +6550,10 @@ func (vm *VM) bootstrap() { // Move Kernel's methods off Object and onto the Kernel module, where MRI // defines them — runs last, once every one of them is defined above. vm.rehomeKernelMethods() + // The operator methods MRI defines and rbgo only ever computed in the opcode + // (Integer#+, String#*, …). The matching SNAPSHOT is taken later, at the end + // of VM construction — see basic_op_redefinition.go and NewWithStderr. + vm.defineBasicOperatorMethods() } // The three sets below are the name → owner/visibility map CRuby gives Kernel. diff --git a/internal/vm/object_model.go b/internal/vm/object_model.go index 992f62e1..8352b8eb 100644 --- a/internal/vm/object_model.go +++ b/internal/vm/object_model.go @@ -2258,7 +2258,7 @@ func (vm *VM) send(recv object.Value, name string, args []object.Value, blk *Pro if o, ok := recv.(*RObject); ok && object.IsNil(o.builtin) { pureUser = true } - if op, ok := operatorOpcode(name); ok && !pureUser { + if op, ok := operatorOpcode(name); ok && !pureUser && !vm.basicOpWasDefined(op, recv) { return vm.binaryOp(op, recv, args[0]) } } diff --git a/internal/vm/prelude_frozen_gen.go b/internal/vm/prelude_frozen_gen.go index 152fdb3e..27f42623 100644 --- a/internal/vm/prelude_frozen_gen.go +++ b/internal/vm/prelude_frozen_gen.go @@ -40,14 +40,13 @@ func embeddedPrelude() *bytecode.ISeq { Children: []*bytecode.ISeq{ &bytecode.ISeq{ Name: "__compare", - Insns: []bytecode.Instr{{Op: 5}, {Op: 11}, {Op: 30, B: 1}, {Op: 12, A: 1}, {Op: 9}, {Op: 11, A: 1}, {Op: 30, A: 1, Flags: 1}, {Op: 28, A: 64}, {Op: 11}, {Op: 12, A: 2}, {Op: 9}, {Op: 33, A: 2}, {Op: 11, A: 2}, {Op: 30, A: 3, B: 1}, {Op: 27, A: 36}, {Op: 33, A: 4}, {Op: 11, A: 2}, {Op: 30, A: 3, B: 1}, {Op: 27, A: 36}, {Op: 33, A: 5}, {Op: 11, A: 2}, {Op: 30, A: 3, B: 1}, {Op: 27, A: 36}, {Op: 2}, {Op: 11, A: 2}, {Op: 30, A: 3, B: 1}, {Op: 27, A: 36}, {Op: 3}, {Op: 11, A: 2}, {Op: 30, A: 3, B: 1}, {Op: 27, A: 36}, {Op: 4}, {Op: 11, A: 2}, {Op: 30, A: 3, B: 1}, {Op: 27, A: 36}, {Op: 26, A: 39}, {Op: 11}, {Op: 30, A: 6, Flags: 1}, {Op: 26, A: 41}, {Op: 11}, {Op: 30, A: 7, Flags: 1}, {Op: 12, A: 3}, {Op: 9}, {Op: 5}, {Op: 33, A: 8}, {Op: 1}, {Op: 1, A: 1}, {Op: 30, A: 9}, {Op: 13}, {Op: 5}, {Op: 30, A: 7}, {Op: 30, A: 9}, {Op: 13}, {Op: 1, A: 2}, {Op: 30, A: 9}, {Op: 13}, {Op: 11, A: 3}, {Op: 30, A: 9}, {Op: 13}, {Op: 1, A: 3}, {Op: 30, A: 9}, {Op: 13}, {Op: 30, A: 10, B: 2, Flags: 2}, {Op: 26, A: 65}, {Op: 2}, {Op: 9}, {Op: 11, A: 1}, {Op: 53}}, + Insns: []bytecode.Instr{{Op: 5}, {Op: 11}, {Op: 30, B: 1}, {Op: 12, A: 1}, {Op: 9}, {Op: 11, A: 1}, {Op: 30, A: 1, Flags: 1}, {Op: 28, A: 56}, {Op: 11}, {Op: 12, A: 2}, {Op: 9}, {Op: 33, A: 2}, {Op: 11, A: 2}, {Op: 30, A: 3, B: 1}, {Op: 27, A: 36}, {Op: 33, A: 4}, {Op: 11, A: 2}, {Op: 30, A: 3, B: 1}, {Op: 27, A: 36}, {Op: 33, A: 5}, {Op: 11, A: 2}, {Op: 30, A: 3, B: 1}, {Op: 27, A: 36}, {Op: 2}, {Op: 11, A: 2}, {Op: 30, A: 3, B: 1}, {Op: 27, A: 36}, {Op: 3}, {Op: 11, A: 2}, {Op: 30, A: 3, B: 1}, {Op: 27, A: 36}, {Op: 4}, {Op: 11, A: 2}, {Op: 30, A: 3, B: 1}, {Op: 27, A: 36}, {Op: 26, A: 39}, {Op: 11}, {Op: 30, A: 6, Flags: 1}, {Op: 26, A: 41}, {Op: 11}, {Op: 30, A: 7, Flags: 1}, {Op: 12, A: 3}, {Op: 9}, {Op: 5}, {Op: 33, A: 8}, {Op: 1}, {Op: 5}, {Op: 30, A: 7}, {Op: 93}, {Op: 1, A: 1}, {Op: 11, A: 3}, {Op: 93}, {Op: 1, A: 2}, {Op: 92, A: 5}, {Op: 30, A: 9, B: 2, Flags: 2}, {Op: 26, A: 57}, {Op: 2}, {Op: 9}, {Op: 11, A: 1}, {Op: 53}}, Consts: []object.Value{ - object.NewString(""), object.FString("comparison of ", ""), object.FString(" with ", ""), object.FString(" failed", ""), }, - Names: []string{"<=>", "nil?", "Integer", "===", "Float", "Symbol", "inspect", "class", "ArgumentError", "to_s", "raise"}, + Names: []string{"<=>", "nil?", "Integer", "===", "Float", "Symbol", "inspect", "class", "ArgumentError", "raise"}, Params: []string{"other"}, NumRequired: 1, SplatIndex: -1, @@ -56,7 +55,7 @@ func embeddedPrelude() *bytecode.ISeq { BlockSlot: -1, NumLocals: 4, Locals: []string{"other", "cmp", "", "right"}, - Lines: []bytecode.LineEntry{{PC: 0, Line: 109}, {PC: 5, Line: 18}, {PC: 8, Line: 19}, {PC: 23, Line: 259}, {PC: 36, Line: 21}, {PC: 39, Line: 23}, {PC: 43, Line: 25}, {PC: 49, Line: 109}, {PC: 56, Line: 25}, {PC: 66, Line: 27}}, + Lines: []bytecode.LineEntry{{PC: 0, Line: 109}, {PC: 5, Line: 18}, {PC: 8, Line: 19}, {PC: 23, Line: 259}, {PC: 36, Line: 21}, {PC: 39, Line: 23}, {PC: 43, Line: 25}, {PC: 46, Line: 109}, {PC: 50, Line: 25}, {PC: 58, Line: 27}}, FirstLine: 16, Super: "", }, @@ -172,26 +171,22 @@ func embeddedPrelude() *bytecode.ISeq { }, &bytecode.ISeq{ Name: "clamp", - Insns: []bytecode.Instr{{Op: 11}, {Op: 30, Flags: 1}, {Op: 1}, {Op: 22}, {Op: 10}, {Op: 28, A: 12}, {Op: 9}, {Op: 11}, {Op: 1, A: 1}, {Op: 30, A: 1, B: 1, Flags: 3}, {Op: 33, A: 2}, {Op: 30, A: 3, B: 1, Flags: 3}, {Op: 28, A: 43}, {Op: 11}, {Op: 1, A: 1}, {Op: 30, A: 1, B: 1, Flags: 3}, {Op: 12, A: 1}, {Op: 9}, {Op: 11, A: 1}, {Op: 30, A: 4, Flags: 1}, {Op: 10}, {Op: 28, A: 27}, {Op: 9}, {Op: 11, A: 1}, {Op: 30, A: 5, Flags: 1}, {Op: 30, A: 6, Flags: 1}, {Op: 25}, {Op: 28, A: 33}, {Op: 5}, {Op: 33, A: 7}, {Op: 1, A: 2}, {Op: 30, A: 8, B: 2, Flags: 2}, {Op: 26, A: 34}, {Op: 2}, {Op: 9}, {Op: 11, A: 1}, {Op: 30, A: 9, Flags: 1}, {Op: 12, A: 2}, {Op: 9}, {Op: 11, A: 1}, {Op: 30, A: 5, Flags: 1}, {Op: 12, A: 3}, {Op: 26, A: 92}, {Op: 11}, {Op: 30, Flags: 1}, {Op: 1, A: 3}, {Op: 22}, {Op: 28, A: 56}, {Op: 11}, {Op: 10}, {Op: 59, A: 2}, {Op: 12, A: 2}, {Op: 9}, {Op: 12, A: 3}, {Op: 9}, {Op: 26, A: 92}, {Op: 11}, {Op: 30, Flags: 1}, {Op: 1}, {Op: 22}, {Op: 28, A: 78}, {Op: 5}, {Op: 33, A: 10}, {Op: 1, A: 4}, {Op: 1, A: 5}, {Op: 30, A: 11}, {Op: 13}, {Op: 11}, {Op: 1, A: 1}, {Op: 30, A: 1, B: 1, Flags: 3}, {Op: 30, A: 12, Flags: 1}, {Op: 30, A: 11}, {Op: 13}, {Op: 1, A: 6}, {Op: 30, A: 11}, {Op: 13}, {Op: 30, A: 8, B: 2, Flags: 2}, {Op: 26, A: 92}, {Op: 5}, {Op: 33, A: 7}, {Op: 1, A: 7}, {Op: 1, A: 8}, {Op: 30, A: 11}, {Op: 13}, {Op: 11}, {Op: 30, Flags: 1}, {Op: 30, A: 11}, {Op: 13}, {Op: 1, A: 9}, {Op: 30, A: 11}, {Op: 13}, {Op: 30, A: 8, B: 2, Flags: 2}, {Op: 9}, {Op: 11, A: 2}, {Op: 30, A: 6, Flags: 1}, {Op: 25}, {Op: 10}, {Op: 28, A: 102}, {Op: 9}, {Op: 11, A: 3}, {Op: 30, A: 6, Flags: 1}, {Op: 25}, {Op: 28, A: 124}, {Op: 11, A: 2}, {Op: 11, A: 3}, {Op: 30, A: 13, B: 1}, {Op: 12, A: 4}, {Op: 9}, {Op: 11, A: 4}, {Op: 30, A: 6, Flags: 1}, {Op: 10}, {Op: 27, A: 116}, {Op: 9}, {Op: 11, A: 4}, {Op: 1, A: 1}, {Op: 19}, {Op: 28, A: 122}, {Op: 5}, {Op: 33, A: 7}, {Op: 1, A: 10}, {Op: 30, A: 8, B: 2, Flags: 2}, {Op: 26, A: 123}, {Op: 2}, {Op: 26, A: 125}, {Op: 2}, {Op: 9}, {Op: 11, A: 2}, {Op: 30, A: 6, Flags: 1}, {Op: 25}, {Op: 28, A: 170}, {Op: 5}, {Op: 11, A: 2}, {Op: 30, A: 13, B: 1}, {Op: 12, A: 4}, {Op: 9}, {Op: 11, A: 4}, {Op: 30, A: 6, Flags: 1}, {Op: 28, A: 159}, {Op: 5}, {Op: 33, A: 7}, {Op: 1, A: 11}, {Op: 1, A: 12}, {Op: 30, A: 11}, {Op: 13}, {Op: 5}, {Op: 30, A: 12}, {Op: 30, A: 11}, {Op: 13}, {Op: 1, A: 13}, {Op: 30, A: 11}, {Op: 13}, {Op: 11, A: 2}, {Op: 30, A: 11}, {Op: 13}, {Op: 1, A: 14}, {Op: 30, A: 11}, {Op: 13}, {Op: 30, A: 8, B: 2, Flags: 2}, {Op: 26, A: 160}, {Op: 2}, {Op: 9}, {Op: 11, A: 4}, {Op: 1, A: 1}, {Op: 18}, {Op: 28, A: 168}, {Op: 11, A: 2}, {Op: 53}, {Op: 26, A: 169}, {Op: 2}, {Op: 26, A: 171}, {Op: 2}, {Op: 9}, {Op: 11, A: 3}, {Op: 30, A: 6, Flags: 1}, {Op: 25}, {Op: 28, A: 216}, {Op: 5}, {Op: 11, A: 3}, {Op: 30, A: 13, B: 1}, {Op: 12, A: 4}, {Op: 9}, {Op: 11, A: 4}, {Op: 30, A: 6, Flags: 1}, {Op: 28, A: 205}, {Op: 5}, {Op: 33, A: 7}, {Op: 1, A: 15}, {Op: 1, A: 12}, {Op: 30, A: 11}, {Op: 13}, {Op: 5}, {Op: 30, A: 12}, {Op: 30, A: 11}, {Op: 13}, {Op: 1, A: 13}, {Op: 30, A: 11}, {Op: 13}, {Op: 11, A: 3}, {Op: 30, A: 11}, {Op: 13}, {Op: 1, A: 14}, {Op: 30, A: 11}, {Op: 13}, {Op: 30, A: 8, B: 2, Flags: 2}, {Op: 26, A: 206}, {Op: 2}, {Op: 9}, {Op: 11, A: 4}, {Op: 1, A: 1}, {Op: 19}, {Op: 28, A: 214}, {Op: 11, A: 3}, {Op: 53}, {Op: 26, A: 215}, {Op: 2}, {Op: 26, A: 217}, {Op: 2}, {Op: 9}, {Op: 5}, {Op: 53}}, + Insns: []bytecode.Instr{{Op: 11}, {Op: 30, Flags: 1}, {Op: 1}, {Op: 22}, {Op: 10}, {Op: 28, A: 12}, {Op: 9}, {Op: 11}, {Op: 1, A: 1}, {Op: 30, A: 1, B: 1, Flags: 3}, {Op: 33, A: 2}, {Op: 30, A: 3, B: 1, Flags: 3}, {Op: 28, A: 43}, {Op: 11}, {Op: 1, A: 1}, {Op: 30, A: 1, B: 1, Flags: 3}, {Op: 12, A: 1}, {Op: 9}, {Op: 11, A: 1}, {Op: 30, A: 4, Flags: 1}, {Op: 10}, {Op: 28, A: 27}, {Op: 9}, {Op: 11, A: 1}, {Op: 30, A: 5, Flags: 1}, {Op: 30, A: 6, Flags: 1}, {Op: 25}, {Op: 28, A: 33}, {Op: 5}, {Op: 33, A: 7}, {Op: 1, A: 2}, {Op: 30, A: 8, B: 2, Flags: 2}, {Op: 26, A: 34}, {Op: 2}, {Op: 9}, {Op: 11, A: 1}, {Op: 30, A: 9, Flags: 1}, {Op: 12, A: 2}, {Op: 9}, {Op: 11, A: 1}, {Op: 30, A: 5, Flags: 1}, {Op: 12, A: 3}, {Op: 26, A: 82}, {Op: 11}, {Op: 30, Flags: 1}, {Op: 1, A: 3}, {Op: 22}, {Op: 28, A: 56}, {Op: 11}, {Op: 10}, {Op: 59, A: 2}, {Op: 12, A: 2}, {Op: 9}, {Op: 12, A: 3}, {Op: 9}, {Op: 26, A: 82}, {Op: 11}, {Op: 30, Flags: 1}, {Op: 1}, {Op: 22}, {Op: 28, A: 73}, {Op: 5}, {Op: 33, A: 10}, {Op: 1, A: 4}, {Op: 11}, {Op: 1, A: 1}, {Op: 30, A: 1, B: 1, Flags: 3}, {Op: 30, A: 11, Flags: 1}, {Op: 93}, {Op: 1, A: 5}, {Op: 92, A: 3}, {Op: 30, A: 8, B: 2, Flags: 2}, {Op: 26, A: 82}, {Op: 5}, {Op: 33, A: 7}, {Op: 1, A: 6}, {Op: 11}, {Op: 30, Flags: 1}, {Op: 93}, {Op: 1, A: 7}, {Op: 92, A: 3}, {Op: 30, A: 8, B: 2, Flags: 2}, {Op: 9}, {Op: 11, A: 2}, {Op: 30, A: 6, Flags: 1}, {Op: 25}, {Op: 10}, {Op: 28, A: 92}, {Op: 9}, {Op: 11, A: 3}, {Op: 30, A: 6, Flags: 1}, {Op: 25}, {Op: 28, A: 114}, {Op: 11, A: 2}, {Op: 11, A: 3}, {Op: 30, A: 12, B: 1}, {Op: 12, A: 4}, {Op: 9}, {Op: 11, A: 4}, {Op: 30, A: 6, Flags: 1}, {Op: 10}, {Op: 27, A: 106}, {Op: 9}, {Op: 11, A: 4}, {Op: 1, A: 1}, {Op: 19}, {Op: 28, A: 112}, {Op: 5}, {Op: 33, A: 7}, {Op: 1, A: 8}, {Op: 30, A: 8, B: 2, Flags: 2}, {Op: 26, A: 113}, {Op: 2}, {Op: 26, A: 115}, {Op: 2}, {Op: 9}, {Op: 11, A: 2}, {Op: 30, A: 6, Flags: 1}, {Op: 25}, {Op: 28, A: 152}, {Op: 5}, {Op: 11, A: 2}, {Op: 30, A: 12, B: 1}, {Op: 12, A: 4}, {Op: 9}, {Op: 11, A: 4}, {Op: 30, A: 6, Flags: 1}, {Op: 28, A: 141}, {Op: 5}, {Op: 33, A: 7}, {Op: 1, A: 9}, {Op: 5}, {Op: 30, A: 11}, {Op: 93}, {Op: 1, A: 10}, {Op: 11, A: 2}, {Op: 93}, {Op: 1, A: 11}, {Op: 92, A: 5}, {Op: 30, A: 8, B: 2, Flags: 2}, {Op: 26, A: 142}, {Op: 2}, {Op: 9}, {Op: 11, A: 4}, {Op: 1, A: 1}, {Op: 18}, {Op: 28, A: 150}, {Op: 11, A: 2}, {Op: 53}, {Op: 26, A: 151}, {Op: 2}, {Op: 26, A: 153}, {Op: 2}, {Op: 9}, {Op: 11, A: 3}, {Op: 30, A: 6, Flags: 1}, {Op: 25}, {Op: 28, A: 190}, {Op: 5}, {Op: 11, A: 3}, {Op: 30, A: 12, B: 1}, {Op: 12, A: 4}, {Op: 9}, {Op: 11, A: 4}, {Op: 30, A: 6, Flags: 1}, {Op: 28, A: 179}, {Op: 5}, {Op: 33, A: 7}, {Op: 1, A: 9}, {Op: 5}, {Op: 30, A: 11}, {Op: 93}, {Op: 1, A: 10}, {Op: 11, A: 3}, {Op: 93}, {Op: 1, A: 11}, {Op: 92, A: 5}, {Op: 30, A: 8, B: 2, Flags: 2}, {Op: 26, A: 180}, {Op: 2}, {Op: 9}, {Op: 11, A: 4}, {Op: 1, A: 1}, {Op: 19}, {Op: 28, A: 188}, {Op: 11, A: 3}, {Op: 53}, {Op: 26, A: 189}, {Op: 2}, {Op: 26, A: 191}, {Op: 2}, {Op: 9}, {Op: 5}, {Op: 53}}, Consts: []object.Value{ object.IntValue(1), object.IntValue(0), object.FString("cannot clamp with an exclusive range", ""), object.IntValue(2), - object.NewString(""), object.FString("wrong argument type ", ""), object.FString(" (expected Range)", ""), - object.NewString(""), object.FString("wrong number of arguments (given ", ""), object.FString(", expected 1..2)", ""), object.FString("min argument must be less than or equal to max argument", ""), - object.NewString(""), object.FString("comparison of ", ""), object.FString(" with ", ""), object.FString(" failed", ""), - object.NewString(""), }, - Names: []string{"size", "[]", "Range", "is_a?", "exclude_end?", "end", "nil?", "ArgumentError", "raise", "begin", "TypeError", "to_s", "class", "<=>"}, + Names: []string{"size", "[]", "Range", "is_a?", "exclude_end?", "end", "nil?", "ArgumentError", "raise", "begin", "TypeError", "class", "<=>"}, Params: []string{"args"}, NumRequired: 0, SplatIndex: 0, @@ -200,7 +195,7 @@ func embeddedPrelude() *bytecode.ISeq { BlockSlot: -1, NumLocals: 5, Locals: []string{"args", "range", "min", "max", "c"}, - Lines: []bytecode.LineEntry{{PC: 0, Line: 75}, {PC: 13, Line: 76}, {PC: 18, Line: 79}, {PC: 28, Line: 80}, {PC: 35, Line: 82}, {PC: 39, Line: 83}, {PC: 48, Line: 85}, {PC: 61, Line: 87}, {PC: 78, Line: 89}, {PC: 93, Line: 93}, {PC: 103, Line: 94}, {PC: 108, Line: 95}, {PC: 117, Line: 96}, {PC: 126, Line: 99}, {PC: 130, Line: 109}, {PC: 135, Line: 101}, {PC: 144, Line: 109}, {PC: 151, Line: 101}, {PC: 161, Line: 102}, {PC: 172, Line: 104}, {PC: 176, Line: 109}, {PC: 181, Line: 106}, {PC: 190, Line: 109}, {PC: 197, Line: 106}, {PC: 207, Line: 107}, {PC: 218, Line: 109}}, + Lines: []bytecode.LineEntry{{PC: 0, Line: 75}, {PC: 13, Line: 76}, {PC: 18, Line: 79}, {PC: 28, Line: 80}, {PC: 35, Line: 82}, {PC: 39, Line: 83}, {PC: 48, Line: 85}, {PC: 61, Line: 87}, {PC: 73, Line: 89}, {PC: 83, Line: 93}, {PC: 93, Line: 94}, {PC: 98, Line: 95}, {PC: 107, Line: 96}, {PC: 116, Line: 99}, {PC: 120, Line: 109}, {PC: 125, Line: 101}, {PC: 131, Line: 109}, {PC: 135, Line: 101}, {PC: 143, Line: 102}, {PC: 154, Line: 104}, {PC: 158, Line: 109}, {PC: 163, Line: 106}, {PC: 169, Line: 109}, {PC: 173, Line: 106}, {PC: 181, Line: 107}, {PC: 192, Line: 109}}, FirstLine: 74, Super: "", }, @@ -279,22 +274,20 @@ func embeddedPrelude() *bytecode.ISeq { }, &bytecode.ISeq{ Name: "__enum_int_arg", - Insns: []bytecode.Instr{{Op: 11}, {Op: 33}, {Op: 30, A: 1, B: 1, Flags: 3}, {Op: 28, A: 7}, {Op: 11}, {Op: 12, A: 1}, {Op: 26, A: 73}, {Op: 11}, {Op: 33, A: 2}, {Op: 30, A: 1, B: 1, Flags: 3}, {Op: 28, A: 15}, {Op: 11}, {Op: 30, A: 3, Flags: 1}, {Op: 12, A: 1}, {Op: 26, A: 73}, {Op: 11}, {Op: 1}, {Op: 30, A: 4, B: 1, Flags: 3}, {Op: 28, A: 59}, {Op: 11}, {Op: 30, A: 3, Flags: 1}, {Op: 12, A: 1}, {Op: 9}, {Op: 11, A: 1}, {Op: 33}, {Op: 30, A: 1, B: 1, Flags: 3}, {Op: 25}, {Op: 28, A: 57}, {Op: 5}, {Op: 33, A: 5}, {Op: 1, A: 1}, {Op: 1, A: 2}, {Op: 30, A: 6}, {Op: 13}, {Op: 11}, {Op: 30, A: 7, Flags: 1}, {Op: 30, A: 6}, {Op: 13}, {Op: 1, A: 3}, {Op: 30, A: 6}, {Op: 13}, {Op: 11}, {Op: 30, A: 7, Flags: 1}, {Op: 30, A: 6}, {Op: 13}, {Op: 1, A: 4}, {Op: 30, A: 6}, {Op: 13}, {Op: 11, A: 1}, {Op: 30, A: 7, Flags: 1}, {Op: 30, A: 6}, {Op: 13}, {Op: 1, A: 5}, {Op: 30, A: 6}, {Op: 13}, {Op: 30, A: 8, B: 2, Flags: 2}, {Op: 26, A: 58}, {Op: 2}, {Op: 26, A: 73}, {Op: 5}, {Op: 33, A: 5}, {Op: 1, A: 6}, {Op: 1, A: 7}, {Op: 30, A: 6}, {Op: 13}, {Op: 11}, {Op: 30, A: 7, Flags: 1}, {Op: 30, A: 6}, {Op: 13}, {Op: 1, A: 8}, {Op: 30, A: 6}, {Op: 13}, {Op: 30, A: 8, B: 2, Flags: 2}, {Op: 9}, {Op: 11, A: 1}, {Op: 1, A: 9}, {Op: 19}, {Op: 10}, {Op: 27, A: 83}, {Op: 9}, {Op: 11, A: 1}, {Op: 1, A: 10}, {Op: 18}, {Op: 28, A: 89}, {Op: 5}, {Op: 33, A: 9}, {Op: 1, A: 11}, {Op: 30, A: 8, B: 2, Flags: 2}, {Op: 26, A: 90}, {Op: 2}, {Op: 9}, {Op: 11, A: 1}, {Op: 53}}, + Insns: []bytecode.Instr{{Op: 11}, {Op: 33}, {Op: 30, A: 1, B: 1, Flags: 3}, {Op: 28, A: 7}, {Op: 11}, {Op: 12, A: 1}, {Op: 26, A: 57}, {Op: 11}, {Op: 33, A: 2}, {Op: 30, A: 1, B: 1, Flags: 3}, {Op: 28, A: 15}, {Op: 11}, {Op: 30, A: 3, Flags: 1}, {Op: 12, A: 1}, {Op: 26, A: 57}, {Op: 11}, {Op: 1}, {Op: 30, A: 4, B: 1, Flags: 3}, {Op: 28, A: 48}, {Op: 11}, {Op: 30, A: 3, Flags: 1}, {Op: 12, A: 1}, {Op: 9}, {Op: 11, A: 1}, {Op: 33}, {Op: 30, A: 1, B: 1, Flags: 3}, {Op: 25}, {Op: 28, A: 46}, {Op: 5}, {Op: 33, A: 5}, {Op: 1, A: 1}, {Op: 11}, {Op: 30, A: 6, Flags: 1}, {Op: 93}, {Op: 1, A: 2}, {Op: 11}, {Op: 30, A: 6, Flags: 1}, {Op: 93}, {Op: 1, A: 3}, {Op: 11, A: 1}, {Op: 30, A: 6, Flags: 1}, {Op: 93}, {Op: 1, A: 4}, {Op: 92, A: 7}, {Op: 30, A: 7, B: 2, Flags: 2}, {Op: 26, A: 47}, {Op: 2}, {Op: 26, A: 57}, {Op: 5}, {Op: 33, A: 5}, {Op: 1, A: 5}, {Op: 11}, {Op: 30, A: 6, Flags: 1}, {Op: 93}, {Op: 1, A: 6}, {Op: 92, A: 3}, {Op: 30, A: 7, B: 2, Flags: 2}, {Op: 9}, {Op: 11, A: 1}, {Op: 1, A: 7}, {Op: 19}, {Op: 10}, {Op: 27, A: 67}, {Op: 9}, {Op: 11, A: 1}, {Op: 1, A: 8}, {Op: 18}, {Op: 28, A: 73}, {Op: 5}, {Op: 33, A: 8}, {Op: 1, A: 9}, {Op: 30, A: 7, B: 2, Flags: 2}, {Op: 26, A: 74}, {Op: 2}, {Op: 9}, {Op: 11, A: 1}, {Op: 53}}, Consts: []object.Value{ object.Symbol("to_int"), - object.NewString(""), object.FString("can't convert ", ""), object.FString(" to Integer (", ""), object.FString("#to_int gives ", ""), object.FString(")", ""), - object.NewString(""), object.FString("no implicit conversion of ", ""), object.FString(" into Integer", ""), object.IntValue(9223372036854775807), &object.Bignum{I: frozenBig("-9223372036854775808")}, object.FString("bignum too big to convert into 'long'", ""), }, - Names: []string{"Integer", "is_a?", "Float", "to_int", "respond_to?", "TypeError", "to_s", "class", "raise", "RangeError"}, + Names: []string{"Integer", "is_a?", "Float", "to_int", "respond_to?", "TypeError", "class", "raise", "RangeError"}, Params: []string{"n"}, NumRequired: 1, SplatIndex: -1, @@ -303,7 +296,7 @@ func embeddedPrelude() *bytecode.ISeq { BlockSlot: -1, NumLocals: 2, Locals: []string{"n", "v"}, - Lines: []bytecode.LineEntry{{PC: 0, Line: 140}, {PC: 4, Line: 141}, {PC: 11, Line: 143}, {PC: 19, Line: 145}, {PC: 23, Line: 146}, {PC: 59, Line: 148}, {PC: 74, Line: 150}, {PC: 91, Line: 151}}, + Lines: []bytecode.LineEntry{{PC: 0, Line: 140}, {PC: 4, Line: 141}, {PC: 11, Line: 143}, {PC: 19, Line: 145}, {PC: 23, Line: 146}, {PC: 48, Line: 148}, {PC: 58, Line: 150}, {PC: 75, Line: 151}}, FirstLine: 139, Super: "", }, @@ -374,19 +367,17 @@ func embeddedPrelude() *bytecode.ISeq { Children: []*bytecode.ISeq{ &bytecode.ISeq{ Name: "block in to_h", - Insns: []bytecode.Instr{{Op: 52}, {Op: 28, A: 8}, {Op: 6}, {Op: 11}, {Op: 60}, {Op: 61}, {Op: 49, Flags: 2}, {Op: 26, A: 11}, {Op: 5}, {Op: 11}, {Op: 30, B: 1, Flags: 2}, {Op: 12, A: 1}, {Op: 9}, {Op: 33, A: 1}, {Op: 11, A: 1}, {Op: 30, A: 2, B: 1, Flags: 3}, {Op: 12, A: 2}, {Op: 9}, {Op: 11, A: 2}, {Op: 30, A: 3, Flags: 1}, {Op: 28, A: 36}, {Op: 5}, {Op: 33, A: 4}, {Op: 1}, {Op: 1, A: 1}, {Op: 30, A: 5}, {Op: 13}, {Op: 11, A: 1}, {Op: 30, A: 6, Flags: 1}, {Op: 30, A: 5}, {Op: 13}, {Op: 1, A: 2}, {Op: 30, A: 5}, {Op: 13}, {Op: 30, A: 7, B: 2, Flags: 2}, {Op: 26, A: 37}, {Op: 2}, {Op: 9}, {Op: 11, A: 2}, {Op: 30, A: 8, Flags: 1}, {Op: 1, A: 3}, {Op: 22}, {Op: 25}, {Op: 28, A: 59}, {Op: 5}, {Op: 33, A: 9}, {Op: 1, A: 4}, {Op: 1, A: 5}, {Op: 30, A: 5}, {Op: 13}, {Op: 11, A: 2}, {Op: 30, A: 8, Flags: 1}, {Op: 30, A: 5}, {Op: 13}, {Op: 1, A: 6}, {Op: 30, A: 5}, {Op: 13}, {Op: 30, A: 7, B: 2, Flags: 2}, {Op: 26, A: 60}, {Op: 2}, {Op: 9}, {Op: 11, A: 1, B: 1}, {Op: 11, A: 2}, {Op: 1, A: 7}, {Op: 30, A: 10, B: 1, Flags: 3}, {Op: 11, A: 2}, {Op: 1, A: 8}, {Op: 30, A: 10, B: 1, Flags: 3}, {Op: 12, A: 3}, {Op: 30, A: 11, B: 2, Flags: 3}, {Op: 9}, {Op: 11, A: 3}, {Op: 53}}, + Insns: []bytecode.Instr{{Op: 52}, {Op: 28, A: 8}, {Op: 6}, {Op: 11}, {Op: 60}, {Op: 61}, {Op: 49, Flags: 2}, {Op: 26, A: 11}, {Op: 5}, {Op: 11}, {Op: 30, B: 1, Flags: 2}, {Op: 12, A: 1}, {Op: 9}, {Op: 33, A: 1}, {Op: 11, A: 1}, {Op: 30, A: 2, B: 1, Flags: 3}, {Op: 12, A: 2}, {Op: 9}, {Op: 11, A: 2}, {Op: 30, A: 3, Flags: 1}, {Op: 28, A: 31}, {Op: 5}, {Op: 33, A: 4}, {Op: 1}, {Op: 11, A: 1}, {Op: 30, A: 5, Flags: 1}, {Op: 93}, {Op: 1, A: 1}, {Op: 92, A: 3}, {Op: 30, A: 6, B: 2, Flags: 2}, {Op: 26, A: 32}, {Op: 2}, {Op: 9}, {Op: 11, A: 2}, {Op: 30, A: 7, Flags: 1}, {Op: 1, A: 2}, {Op: 22}, {Op: 25}, {Op: 28, A: 49}, {Op: 5}, {Op: 33, A: 8}, {Op: 1, A: 3}, {Op: 11, A: 2}, {Op: 30, A: 7, Flags: 1}, {Op: 93}, {Op: 1, A: 4}, {Op: 92, A: 3}, {Op: 30, A: 6, B: 2, Flags: 2}, {Op: 26, A: 50}, {Op: 2}, {Op: 9}, {Op: 11, A: 1, B: 1}, {Op: 11, A: 2}, {Op: 1, A: 5}, {Op: 30, A: 9, B: 1, Flags: 3}, {Op: 11, A: 2}, {Op: 1, A: 6}, {Op: 30, A: 9, B: 1, Flags: 3}, {Op: 12, A: 3}, {Op: 30, A: 10, B: 2, Flags: 3}, {Op: 9}, {Op: 11, A: 3}, {Op: 53}}, Consts: []object.Value{ - object.NewString(""), object.FString("wrong element type ", ""), object.FString(" (expected array)", ""), object.IntValue(2), - object.NewString(""), object.FString("element has wrong array length (expected 2, was ", ""), object.FString(")", ""), object.IntValue(0), object.IntValue(1), }, - Names: []string{"__pack", "Array", "try_convert", "nil?", "TypeError", "to_s", "class", "raise", "length", "ArgumentError", "[]", "[]="}, + Names: []string{"__pack", "Array", "try_convert", "nil?", "TypeError", "class", "raise", "length", "ArgumentError", "[]", "[]="}, Params: []string{"a"}, NumRequired: 0, SplatIndex: 0, @@ -395,7 +386,7 @@ func embeddedPrelude() *bytecode.ISeq { BlockSlot: -1, NumLocals: 4, Locals: []string{"a", "pair", "ary", ""}, - Lines: []bytecode.LineEntry{{PC: 0, Line: 179}, {PC: 13, Line: 180}, {PC: 18, Line: 181}, {PC: 38, Line: 182}, {PC: 61, Line: 183}}, + Lines: []bytecode.LineEntry{{PC: 0, Line: 179}, {PC: 13, Line: 180}, {PC: 18, Line: 181}, {PC: 33, Line: 182}, {PC: 51, Line: 183}}, FirstLine: 178, Super: "", }, @@ -457,17 +448,16 @@ func embeddedPrelude() *bytecode.ISeq { }, &bytecode.ISeq{ Name: "count", - Insns: []bytecode.Instr{{Op: 11}, {Op: 30, Flags: 1}, {Op: 1}, {Op: 19}, {Op: 28, A: 20}, {Op: 5}, {Op: 33, A: 1}, {Op: 1, A: 1}, {Op: 1, A: 2}, {Op: 30, A: 2}, {Op: 13}, {Op: 11}, {Op: 30, Flags: 1}, {Op: 30, A: 2}, {Op: 13}, {Op: 1, A: 3}, {Op: 30, A: 2}, {Op: 13}, {Op: 30, A: 3, B: 2, Flags: 2}, {Op: 26, A: 21}, {Op: 2}, {Op: 9}, {Op: 1, A: 4}, {Op: 12, A: 1}, {Op: 9}, {Op: 11}, {Op: 30, A: 4, Flags: 1}, {Op: 25}, {Op: 28, A: 48}, {Op: 52}, {Op: 28, A: 38}, {Op: 5}, {Op: 1, A: 5}, {Op: 1, A: 6}, {Op: 1}, {Op: 7, A: 1}, {Op: 30, A: 5, B: 2}, {Op: 26, A: 39}, {Op: 2}, {Op: 9}, {Op: 11}, {Op: 1, A: 4}, {Op: 30, A: 6, B: 1, Flags: 3}, {Op: 12, A: 2}, {Op: 9}, {Op: 5}, {Op: 30, A: 7, C: 1}, {Op: 26, A: 55}, {Op: 52}, {Op: 28, A: 53}, {Op: 5}, {Op: 30, A: 7, C: 2}, {Op: 26, A: 55}, {Op: 5}, {Op: 30, A: 7, C: 3}, {Op: 9}, {Op: 11, A: 1}, {Op: 53}}, + Insns: []bytecode.Instr{{Op: 11}, {Op: 30, Flags: 1}, {Op: 1}, {Op: 19}, {Op: 28, A: 15}, {Op: 5}, {Op: 33, A: 1}, {Op: 1, A: 1}, {Op: 11}, {Op: 30, Flags: 1}, {Op: 93}, {Op: 1, A: 2}, {Op: 92, A: 3}, {Op: 30, A: 2, B: 2, Flags: 2}, {Op: 26, A: 16}, {Op: 2}, {Op: 9}, {Op: 1, A: 3}, {Op: 12, A: 1}, {Op: 9}, {Op: 11}, {Op: 30, A: 3, Flags: 1}, {Op: 25}, {Op: 28, A: 43}, {Op: 52}, {Op: 28, A: 33}, {Op: 5}, {Op: 1, A: 4}, {Op: 1, A: 5}, {Op: 1}, {Op: 7, A: 1}, {Op: 30, A: 4, B: 2}, {Op: 26, A: 34}, {Op: 2}, {Op: 9}, {Op: 11}, {Op: 1, A: 3}, {Op: 30, A: 5, B: 1, Flags: 3}, {Op: 12, A: 2}, {Op: 9}, {Op: 5}, {Op: 30, A: 6, C: 1}, {Op: 26, A: 50}, {Op: 52}, {Op: 28, A: 48}, {Op: 5}, {Op: 30, A: 6, C: 2}, {Op: 26, A: 50}, {Op: 5}, {Op: 30, A: 6, C: 3}, {Op: 9}, {Op: 11, A: 1}, {Op: 53}}, Consts: []object.Value{ object.IntValue(1), - object.NewString(""), object.FString("wrong number of arguments (given ", ""), object.FString(", expected 0..1)", ""), object.IntValue(0), object.FString("given block not used", ""), object.Symbol("uplevel"), }, - Names: []string{"length", "ArgumentError", "to_s", "raise", "empty?", "warn", "[]", "each"}, + Names: []string{"length", "ArgumentError", "raise", "empty?", "warn", "[]", "each"}, Params: []string{"args"}, NumRequired: 0, SplatIndex: 0, @@ -476,7 +466,7 @@ func embeddedPrelude() *bytecode.ISeq { BlockSlot: -1, NumLocals: 3, Locals: []string{"args", "n", "item"}, - Lines: []bytecode.LineEntry{{PC: 0, Line: 201}, {PC: 22, Line: 202}, {PC: 25, Line: 203}, {PC: 29, Line: 208}, {PC: 40, Line: 209}, {PC: 45, Line: 210}, {PC: 50, Line: 212}, {PC: 53, Line: 214}, {PC: 56, Line: 216}}, + Lines: []bytecode.LineEntry{{PC: 0, Line: 201}, {PC: 17, Line: 202}, {PC: 20, Line: 203}, {PC: 24, Line: 208}, {PC: 35, Line: 209}, {PC: 40, Line: 210}, {PC: 45, Line: 212}, {PC: 48, Line: 214}, {PC: 51, Line: 216}}, FirstLine: 200, Children: []*bytecode.ISeq{ &bytecode.ISeq{ @@ -952,15 +942,14 @@ func embeddedPrelude() *bytecode.ISeq { Children: []*bytecode.ISeq{ &bytecode.ISeq{ Name: "block in min", - Insns: []bytecode.Instr{{Op: 11, A: 3, B: 1}, {Op: 28, A: 8}, {Op: 11}, {Op: 12, A: 2, B: 1}, {Op: 9}, {Op: 4}, {Op: 12, A: 3, B: 1}, {Op: 26, A: 55}, {Op: 11, A: 1, B: 1}, {Op: 28, A: 15}, {Op: 11, A: 1, B: 1}, {Op: 11}, {Op: 11, A: 2, B: 1}, {Op: 30, B: 2, Flags: 3}, {Op: 26, A: 18}, {Op: 11}, {Op: 11, A: 2, B: 1}, {Op: 30, A: 1, B: 1}, {Op: 12, A: 1}, {Op: 9}, {Op: 11, A: 1}, {Op: 30, A: 2, Flags: 1}, {Op: 28, A: 45}, {Op: 5}, {Op: 33, A: 3}, {Op: 1}, {Op: 1, A: 1}, {Op: 30, A: 4}, {Op: 13}, {Op: 11}, {Op: 30, A: 5, Flags: 1}, {Op: 30, A: 4}, {Op: 13}, {Op: 1, A: 2}, {Op: 30, A: 4}, {Op: 13}, {Op: 11, A: 2, B: 1}, {Op: 30, A: 5, Flags: 1}, {Op: 30, A: 4}, {Op: 13}, {Op: 1, A: 3}, {Op: 30, A: 4}, {Op: 13}, {Op: 30, A: 6, B: 2, Flags: 2}, {Op: 26, A: 46}, {Op: 2}, {Op: 9}, {Op: 11, A: 1}, {Op: 1, A: 4}, {Op: 18}, {Op: 28, A: 54}, {Op: 11}, {Op: 12, A: 2, B: 1}, {Op: 26, A: 55}, {Op: 2}, {Op: 53}}, + Insns: []bytecode.Instr{{Op: 11, A: 3, B: 1}, {Op: 28, A: 8}, {Op: 11}, {Op: 12, A: 2, B: 1}, {Op: 9}, {Op: 4}, {Op: 12, A: 3, B: 1}, {Op: 26, A: 47}, {Op: 11, A: 1, B: 1}, {Op: 28, A: 15}, {Op: 11, A: 1, B: 1}, {Op: 11}, {Op: 11, A: 2, B: 1}, {Op: 30, B: 2, Flags: 3}, {Op: 26, A: 18}, {Op: 11}, {Op: 11, A: 2, B: 1}, {Op: 30, A: 1, B: 1}, {Op: 12, A: 1}, {Op: 9}, {Op: 11, A: 1}, {Op: 30, A: 2, Flags: 1}, {Op: 28, A: 37}, {Op: 5}, {Op: 33, A: 3}, {Op: 1}, {Op: 11}, {Op: 30, A: 4, Flags: 1}, {Op: 93}, {Op: 1, A: 1}, {Op: 11, A: 2, B: 1}, {Op: 30, A: 4, Flags: 1}, {Op: 93}, {Op: 1, A: 2}, {Op: 92, A: 5}, {Op: 30, A: 5, B: 2, Flags: 2}, {Op: 26, A: 38}, {Op: 2}, {Op: 9}, {Op: 11, A: 1}, {Op: 1, A: 3}, {Op: 18}, {Op: 28, A: 46}, {Op: 11}, {Op: 12, A: 2, B: 1}, {Op: 26, A: 47}, {Op: 2}, {Op: 53}}, Consts: []object.Value{ - object.NewString(""), object.FString("comparison of ", ""), object.FString(" with ", ""), object.FString(" failed", ""), object.IntValue(0), }, - Names: []string{"call", "<=>", "nil?", "ArgumentError", "to_s", "class", "raise"}, + Names: []string{"call", "<=>", "nil?", "ArgumentError", "class", "raise"}, Params: []string{"x"}, NumRequired: 1, SplatIndex: -1, @@ -969,7 +958,7 @@ func embeddedPrelude() *bytecode.ISeq { BlockSlot: -1, NumLocals: 2, Locals: []string{"x", "c"}, - Lines: []bytecode.LineEntry{{PC: 0, Line: 315}, {PC: 2, Line: 316}, {PC: 5, Line: 317}, {PC: 8, Line: 319}, {PC: 20, Line: 320}, {PC: 47, Line: 321}}, + Lines: []bytecode.LineEntry{{PC: 0, Line: 315}, {PC: 2, Line: 316}, {PC: 5, Line: 317}, {PC: 8, Line: 319}, {PC: 20, Line: 320}, {PC: 39, Line: 321}}, FirstLine: 314, Super: "", }, @@ -993,15 +982,14 @@ func embeddedPrelude() *bytecode.ISeq { Children: []*bytecode.ISeq{ &bytecode.ISeq{ Name: "block in max", - Insns: []bytecode.Instr{{Op: 11, A: 3, B: 1}, {Op: 28, A: 8}, {Op: 11}, {Op: 12, A: 2, B: 1}, {Op: 9}, {Op: 4}, {Op: 12, A: 3, B: 1}, {Op: 26, A: 55}, {Op: 11, A: 1, B: 1}, {Op: 28, A: 15}, {Op: 11, A: 1, B: 1}, {Op: 11}, {Op: 11, A: 2, B: 1}, {Op: 30, B: 2, Flags: 3}, {Op: 26, A: 18}, {Op: 11}, {Op: 11, A: 2, B: 1}, {Op: 30, A: 1, B: 1}, {Op: 12, A: 1}, {Op: 9}, {Op: 11, A: 1}, {Op: 30, A: 2, Flags: 1}, {Op: 28, A: 45}, {Op: 5}, {Op: 33, A: 3}, {Op: 1}, {Op: 1, A: 1}, {Op: 30, A: 4}, {Op: 13}, {Op: 11}, {Op: 30, A: 5, Flags: 1}, {Op: 30, A: 4}, {Op: 13}, {Op: 1, A: 2}, {Op: 30, A: 4}, {Op: 13}, {Op: 11, A: 2, B: 1}, {Op: 30, A: 5, Flags: 1}, {Op: 30, A: 4}, {Op: 13}, {Op: 1, A: 3}, {Op: 30, A: 4}, {Op: 13}, {Op: 30, A: 6, B: 2, Flags: 2}, {Op: 26, A: 46}, {Op: 2}, {Op: 9}, {Op: 11, A: 1}, {Op: 1, A: 4}, {Op: 19}, {Op: 28, A: 54}, {Op: 11}, {Op: 12, A: 2, B: 1}, {Op: 26, A: 55}, {Op: 2}, {Op: 53}}, + Insns: []bytecode.Instr{{Op: 11, A: 3, B: 1}, {Op: 28, A: 8}, {Op: 11}, {Op: 12, A: 2, B: 1}, {Op: 9}, {Op: 4}, {Op: 12, A: 3, B: 1}, {Op: 26, A: 47}, {Op: 11, A: 1, B: 1}, {Op: 28, A: 15}, {Op: 11, A: 1, B: 1}, {Op: 11}, {Op: 11, A: 2, B: 1}, {Op: 30, B: 2, Flags: 3}, {Op: 26, A: 18}, {Op: 11}, {Op: 11, A: 2, B: 1}, {Op: 30, A: 1, B: 1}, {Op: 12, A: 1}, {Op: 9}, {Op: 11, A: 1}, {Op: 30, A: 2, Flags: 1}, {Op: 28, A: 37}, {Op: 5}, {Op: 33, A: 3}, {Op: 1}, {Op: 11}, {Op: 30, A: 4, Flags: 1}, {Op: 93}, {Op: 1, A: 1}, {Op: 11, A: 2, B: 1}, {Op: 30, A: 4, Flags: 1}, {Op: 93}, {Op: 1, A: 2}, {Op: 92, A: 5}, {Op: 30, A: 5, B: 2, Flags: 2}, {Op: 26, A: 38}, {Op: 2}, {Op: 9}, {Op: 11, A: 1}, {Op: 1, A: 3}, {Op: 19}, {Op: 28, A: 46}, {Op: 11}, {Op: 12, A: 2, B: 1}, {Op: 26, A: 47}, {Op: 2}, {Op: 53}}, Consts: []object.Value{ - object.NewString(""), object.FString("comparison of ", ""), object.FString(" with ", ""), object.FString(" failed", ""), object.IntValue(0), }, - Names: []string{"call", "<=>", "nil?", "ArgumentError", "to_s", "class", "raise"}, + Names: []string{"call", "<=>", "nil?", "ArgumentError", "class", "raise"}, Params: []string{"x"}, NumRequired: 1, SplatIndex: -1, @@ -1010,7 +998,7 @@ func embeddedPrelude() *bytecode.ISeq { BlockSlot: -1, NumLocals: 2, Locals: []string{"x", "c"}, - Lines: []bytecode.LineEntry{{PC: 0, Line: 332}, {PC: 2, Line: 333}, {PC: 5, Line: 334}, {PC: 8, Line: 336}, {PC: 20, Line: 337}, {PC: 47, Line: 338}}, + Lines: []bytecode.LineEntry{{PC: 0, Line: 332}, {PC: 2, Line: 333}, {PC: 5, Line: 334}, {PC: 8, Line: 336}, {PC: 20, Line: 337}, {PC: 39, Line: 338}}, FirstLine: 331, Super: "", }, @@ -1034,10 +1022,9 @@ func embeddedPrelude() *bytecode.ISeq { }, &bytecode.ISeq{ Name: "reduce", - Insns: []bytecode.Instr{{Op: 11}, {Op: 30, Flags: 1}, {Op: 1}, {Op: 19}, {Op: 28, A: 20}, {Op: 5}, {Op: 33, A: 1}, {Op: 1, A: 1}, {Op: 1, A: 2}, {Op: 30, A: 2}, {Op: 13}, {Op: 11}, {Op: 30, Flags: 1}, {Op: 30, A: 2}, {Op: 13}, {Op: 1, A: 3}, {Op: 30, A: 2}, {Op: 13}, {Op: 30, A: 3, B: 2, Flags: 2}, {Op: 26, A: 21}, {Op: 2}, {Op: 9}, {Op: 2}, {Op: 12, A: 1}, {Op: 9}, {Op: 4}, {Op: 12, A: 2}, {Op: 9}, {Op: 2}, {Op: 12, A: 3}, {Op: 9}, {Op: 11}, {Op: 30, Flags: 1}, {Op: 1}, {Op: 22}, {Op: 28, A: 64}, {Op: 52}, {Op: 10}, {Op: 28, A: 41}, {Op: 9}, {Op: 36, A: 4}, {Op: 28, A: 49}, {Op: 5}, {Op: 1, A: 4}, {Op: 1, A: 5}, {Op: 1, A: 6}, {Op: 7, A: 1}, {Op: 30, A: 5, B: 2}, {Op: 26, A: 50}, {Op: 2}, {Op: 9}, {Op: 11}, {Op: 1, A: 7}, {Op: 30, A: 6, B: 1, Flags: 3}, {Op: 12, A: 3}, {Op: 9}, {Op: 11}, {Op: 1, A: 6}, {Op: 30, A: 6, B: 1, Flags: 3}, {Op: 12, A: 1}, {Op: 9}, {Op: 3}, {Op: 12, A: 2}, {Op: 26, A: 93}, {Op: 11}, {Op: 30, Flags: 1}, {Op: 1, A: 6}, {Op: 22}, {Op: 28, A: 84}, {Op: 52}, {Op: 28, A: 79}, {Op: 11}, {Op: 1, A: 7}, {Op: 30, A: 6, B: 1, Flags: 3}, {Op: 12, A: 3}, {Op: 9}, {Op: 3}, {Op: 12, A: 2}, {Op: 26, A: 83}, {Op: 11}, {Op: 1, A: 7}, {Op: 30, A: 6, B: 1, Flags: 3}, {Op: 12, A: 1}, {Op: 26, A: 93}, {Op: 52}, {Op: 25}, {Op: 28, A: 92}, {Op: 5}, {Op: 33, A: 1}, {Op: 1, A: 8}, {Op: 30, A: 3, B: 2, Flags: 2}, {Op: 26, A: 93}, {Op: 2}, {Op: 9}, {Op: 11, A: 1}, {Op: 30, A: 7, Flags: 1}, {Op: 10}, {Op: 27, A: 102}, {Op: 9}, {Op: 11, A: 1}, {Op: 33, A: 8}, {Op: 30, A: 9, B: 1, Flags: 3}, {Op: 10}, {Op: 27, A: 108}, {Op: 9}, {Op: 11, A: 1}, {Op: 33, A: 10}, {Op: 30, A: 9, B: 1, Flags: 3}, {Op: 25}, {Op: 28, A: 133}, {Op: 11, A: 1}, {Op: 1, A: 9}, {Op: 30, A: 11, B: 1, Flags: 3}, {Op: 28, A: 118}, {Op: 11, A: 1}, {Op: 30, A: 12, Flags: 1}, {Op: 12, A: 1}, {Op: 26, A: 132}, {Op: 5}, {Op: 33, A: 13}, {Op: 1, A: 10}, {Op: 1, A: 11}, {Op: 30, A: 2}, {Op: 13}, {Op: 11, A: 1}, {Op: 30, A: 14, Flags: 1}, {Op: 30, A: 2}, {Op: 13}, {Op: 1, A: 12}, {Op: 30, A: 2}, {Op: 13}, {Op: 30, A: 3, B: 2, Flags: 2}, {Op: 26, A: 134}, {Op: 2}, {Op: 9}, {Op: 11, A: 3}, {Op: 12, A: 4}, {Op: 9}, {Op: 11, A: 2}, {Op: 12, A: 5}, {Op: 9}, {Op: 5}, {Op: 30, A: 15, C: 1}, {Op: 9}, {Op: 11, A: 4}, {Op: 53}}, + Insns: []bytecode.Instr{{Op: 11}, {Op: 30, Flags: 1}, {Op: 1}, {Op: 19}, {Op: 28, A: 15}, {Op: 5}, {Op: 33, A: 1}, {Op: 1, A: 1}, {Op: 11}, {Op: 30, Flags: 1}, {Op: 93}, {Op: 1, A: 2}, {Op: 92, A: 3}, {Op: 30, A: 2, B: 2, Flags: 2}, {Op: 26, A: 16}, {Op: 2}, {Op: 9}, {Op: 2}, {Op: 12, A: 1}, {Op: 9}, {Op: 4}, {Op: 12, A: 2}, {Op: 9}, {Op: 2}, {Op: 12, A: 3}, {Op: 9}, {Op: 11}, {Op: 30, Flags: 1}, {Op: 1}, {Op: 22}, {Op: 28, A: 59}, {Op: 52}, {Op: 10}, {Op: 28, A: 36}, {Op: 9}, {Op: 36, A: 3}, {Op: 28, A: 44}, {Op: 5}, {Op: 1, A: 3}, {Op: 1, A: 4}, {Op: 1, A: 5}, {Op: 7, A: 1}, {Op: 30, A: 4, B: 2}, {Op: 26, A: 45}, {Op: 2}, {Op: 9}, {Op: 11}, {Op: 1, A: 6}, {Op: 30, A: 5, B: 1, Flags: 3}, {Op: 12, A: 3}, {Op: 9}, {Op: 11}, {Op: 1, A: 5}, {Op: 30, A: 5, B: 1, Flags: 3}, {Op: 12, A: 1}, {Op: 9}, {Op: 3}, {Op: 12, A: 2}, {Op: 26, A: 88}, {Op: 11}, {Op: 30, Flags: 1}, {Op: 1, A: 5}, {Op: 22}, {Op: 28, A: 79}, {Op: 52}, {Op: 28, A: 74}, {Op: 11}, {Op: 1, A: 6}, {Op: 30, A: 5, B: 1, Flags: 3}, {Op: 12, A: 3}, {Op: 9}, {Op: 3}, {Op: 12, A: 2}, {Op: 26, A: 78}, {Op: 11}, {Op: 1, A: 6}, {Op: 30, A: 5, B: 1, Flags: 3}, {Op: 12, A: 1}, {Op: 26, A: 88}, {Op: 52}, {Op: 25}, {Op: 28, A: 87}, {Op: 5}, {Op: 33, A: 1}, {Op: 1, A: 7}, {Op: 30, A: 2, B: 2, Flags: 2}, {Op: 26, A: 88}, {Op: 2}, {Op: 9}, {Op: 11, A: 1}, {Op: 30, A: 6, Flags: 1}, {Op: 10}, {Op: 27, A: 97}, {Op: 9}, {Op: 11, A: 1}, {Op: 33, A: 7}, {Op: 30, A: 8, B: 1, Flags: 3}, {Op: 10}, {Op: 27, A: 103}, {Op: 9}, {Op: 11, A: 1}, {Op: 33, A: 9}, {Op: 30, A: 8, B: 1, Flags: 3}, {Op: 25}, {Op: 28, A: 123}, {Op: 11, A: 1}, {Op: 1, A: 8}, {Op: 30, A: 10, B: 1, Flags: 3}, {Op: 28, A: 113}, {Op: 11, A: 1}, {Op: 30, A: 11, Flags: 1}, {Op: 12, A: 1}, {Op: 26, A: 122}, {Op: 5}, {Op: 33, A: 12}, {Op: 1, A: 9}, {Op: 11, A: 1}, {Op: 30, A: 13, Flags: 1}, {Op: 93}, {Op: 1, A: 10}, {Op: 92, A: 3}, {Op: 30, A: 2, B: 2, Flags: 2}, {Op: 26, A: 124}, {Op: 2}, {Op: 9}, {Op: 11, A: 3}, {Op: 12, A: 4}, {Op: 9}, {Op: 11, A: 2}, {Op: 12, A: 5}, {Op: 9}, {Op: 5}, {Op: 30, A: 14, C: 1}, {Op: 9}, {Op: 11, A: 4}, {Op: 53}}, Consts: []object.Value{ object.IntValue(2), - object.NewString(""), object.FString("wrong number of arguments (given ", ""), object.FString(", expected 1..2)", ""), object.FString("given block not used", ""), @@ -1046,11 +1033,10 @@ func embeddedPrelude() *bytecode.ISeq { object.IntValue(0), object.FString("wrong number of arguments (given 0, expected 1..2)", ""), object.Symbol("to_str"), - object.NewString(""), object.FString("", ""), object.FString(" is not a symbol nor a string", ""), }, - Names: []string{"length", "ArgumentError", "to_s", "raise", "$VERBOSE", "warn", "[]", "nil?", "Symbol", "is_a?", "String", "respond_to?", "to_str", "TypeError", "inspect", "__each_packed"}, + Names: []string{"length", "ArgumentError", "raise", "$VERBOSE", "warn", "[]", "nil?", "Symbol", "is_a?", "String", "respond_to?", "to_str", "TypeError", "inspect", "__each_packed"}, Params: []string{"args"}, NumRequired: 0, SplatIndex: 0, @@ -1059,7 +1045,7 @@ func embeddedPrelude() *bytecode.ISeq { BlockSlot: -1, NumLocals: 6, Locals: []string{"args", "sym", "has_init", "init", "acc", "started"}, - Lines: []bytecode.LineEntry{{PC: 0, Line: 353}, {PC: 22, Line: 259}, {PC: 25, Line: 355}, {PC: 28, Line: 259}, {PC: 31, Line: 357}, {PC: 36, Line: 362}, {PC: 51, Line: 363}, {PC: 56, Line: 364}, {PC: 61, Line: 365}, {PC: 69, Line: 367}, {PC: 71, Line: 368}, {PC: 76, Line: 369}, {PC: 79, Line: 371}, {PC: 87, Line: 374}, {PC: 94, Line: 376}, {PC: 110, Line: 377}, {PC: 114, Line: 378}, {PC: 118, Line: 380}, {PC: 135, Line: 383}, {PC: 138, Line: 384}, {PC: 141, Line: 385}, {PC: 144, Line: 395}}, + Lines: []bytecode.LineEntry{{PC: 0, Line: 353}, {PC: 17, Line: 259}, {PC: 20, Line: 355}, {PC: 23, Line: 259}, {PC: 26, Line: 357}, {PC: 31, Line: 362}, {PC: 46, Line: 363}, {PC: 51, Line: 364}, {PC: 56, Line: 365}, {PC: 64, Line: 367}, {PC: 66, Line: 368}, {PC: 71, Line: 369}, {PC: 74, Line: 371}, {PC: 82, Line: 374}, {PC: 89, Line: 376}, {PC: 105, Line: 377}, {PC: 109, Line: 378}, {PC: 113, Line: 380}, {PC: 125, Line: 383}, {PC: 128, Line: 384}, {PC: 131, Line: 385}, {PC: 134, Line: 395}}, FirstLine: 348, Children: []*bytecode.ISeq{ &bytecode.ISeq{ @@ -1823,25 +1809,22 @@ func embeddedPrelude() *bytecode.ISeq { }, &bytecode.ISeq{ Name: "tally", - Insns: []bytecode.Instr{{Op: 11}, {Op: 30, Flags: 1}, {Op: 1}, {Op: 19}, {Op: 28, A: 20}, {Op: 5}, {Op: 33, A: 1}, {Op: 1, A: 1}, {Op: 1, A: 2}, {Op: 30, A: 2}, {Op: 13}, {Op: 11}, {Op: 30, Flags: 1}, {Op: 30, A: 2}, {Op: 13}, {Op: 1, A: 3}, {Op: 30, A: 2}, {Op: 13}, {Op: 30, A: 3, B: 2, Flags: 2}, {Op: 26, A: 21}, {Op: 2}, {Op: 9}, {Op: 11}, {Op: 30, A: 4, Flags: 1}, {Op: 28, A: 28}, {Op: 7}, {Op: 12, A: 1}, {Op: 26, A: 110}, {Op: 11}, {Op: 1, A: 4}, {Op: 30, A: 5, B: 1, Flags: 3}, {Op: 12, A: 2}, {Op: 9}, {Op: 11, A: 2}, {Op: 33, A: 6}, {Op: 30, A: 7, B: 1, Flags: 3}, {Op: 25}, {Op: 28, A: 106}, {Op: 11, A: 2}, {Op: 1, A: 5}, {Op: 30, A: 8, B: 1, Flags: 3}, {Op: 28, A: 86}, {Op: 11, A: 2}, {Op: 30, A: 9, Flags: 1}, {Op: 12, A: 2}, {Op: 9}, {Op: 11, A: 2}, {Op: 33, A: 6}, {Op: 30, A: 7, B: 1, Flags: 3}, {Op: 25}, {Op: 28, A: 84}, {Op: 5}, {Op: 33, A: 10}, {Op: 1, A: 6}, {Op: 1, A: 7}, {Op: 30, A: 2}, {Op: 13}, {Op: 11}, {Op: 1, A: 4}, {Op: 30, A: 5, B: 1, Flags: 3}, {Op: 30, A: 11, Flags: 1}, {Op: 30, A: 2}, {Op: 13}, {Op: 1, A: 8}, {Op: 30, A: 2}, {Op: 13}, {Op: 11}, {Op: 1, A: 4}, {Op: 30, A: 5, B: 1, Flags: 3}, {Op: 30, A: 11, Flags: 1}, {Op: 30, A: 2}, {Op: 13}, {Op: 1, A: 9}, {Op: 30, A: 2}, {Op: 13}, {Op: 11, A: 2}, {Op: 30, A: 11, Flags: 1}, {Op: 30, A: 2}, {Op: 13}, {Op: 1, A: 10}, {Op: 30, A: 2}, {Op: 13}, {Op: 30, A: 3, B: 2, Flags: 2}, {Op: 26, A: 85}, {Op: 2}, {Op: 26, A: 105}, {Op: 5}, {Op: 33, A: 10}, {Op: 1, A: 11}, {Op: 1, A: 12}, {Op: 30, A: 2}, {Op: 13}, {Op: 11, A: 2}, {Op: 30, A: 12, Flags: 1}, {Op: 28, A: 97}, {Op: 1, A: 13}, {Op: 26, A: 99}, {Op: 11, A: 2}, {Op: 30, A: 11, Flags: 1}, {Op: 30, A: 2}, {Op: 13}, {Op: 1, A: 14}, {Op: 30, A: 2}, {Op: 13}, {Op: 30, A: 3, B: 2, Flags: 2}, {Op: 26, A: 107}, {Op: 2}, {Op: 9}, {Op: 11, A: 2}, {Op: 12, A: 1}, {Op: 9}, {Op: 5}, {Op: 30, A: 13, C: 1}, {Op: 9}, {Op: 11, A: 1}, {Op: 53}}, + Insns: []bytecode.Instr{{Op: 11}, {Op: 30, Flags: 1}, {Op: 1}, {Op: 19}, {Op: 28, A: 15}, {Op: 5}, {Op: 33, A: 1}, {Op: 1, A: 1}, {Op: 11}, {Op: 30, Flags: 1}, {Op: 93}, {Op: 1, A: 2}, {Op: 92, A: 3}, {Op: 30, A: 2, B: 2, Flags: 2}, {Op: 26, A: 16}, {Op: 2}, {Op: 9}, {Op: 11}, {Op: 30, A: 3, Flags: 1}, {Op: 28, A: 23}, {Op: 7}, {Op: 12, A: 1}, {Op: 26, A: 89}, {Op: 11}, {Op: 1, A: 3}, {Op: 30, A: 4, B: 1, Flags: 3}, {Op: 12, A: 2}, {Op: 9}, {Op: 11, A: 2}, {Op: 33, A: 5}, {Op: 30, A: 6, B: 1, Flags: 3}, {Op: 25}, {Op: 28, A: 85}, {Op: 11, A: 2}, {Op: 1, A: 4}, {Op: 30, A: 7, B: 1, Flags: 3}, {Op: 28, A: 70}, {Op: 11, A: 2}, {Op: 30, A: 8, Flags: 1}, {Op: 12, A: 2}, {Op: 9}, {Op: 11, A: 2}, {Op: 33, A: 5}, {Op: 30, A: 6, B: 1, Flags: 3}, {Op: 25}, {Op: 28, A: 68}, {Op: 5}, {Op: 33, A: 9}, {Op: 1, A: 5}, {Op: 11}, {Op: 1, A: 3}, {Op: 30, A: 4, B: 1, Flags: 3}, {Op: 30, A: 10, Flags: 1}, {Op: 93}, {Op: 1, A: 6}, {Op: 11}, {Op: 1, A: 3}, {Op: 30, A: 4, B: 1, Flags: 3}, {Op: 30, A: 10, Flags: 1}, {Op: 93}, {Op: 1, A: 7}, {Op: 11, A: 2}, {Op: 30, A: 10, Flags: 1}, {Op: 93}, {Op: 1, A: 8}, {Op: 92, A: 7}, {Op: 30, A: 2, B: 2, Flags: 2}, {Op: 26, A: 69}, {Op: 2}, {Op: 26, A: 84}, {Op: 5}, {Op: 33, A: 9}, {Op: 1, A: 9}, {Op: 11, A: 2}, {Op: 30, A: 11, Flags: 1}, {Op: 28, A: 78}, {Op: 1, A: 10}, {Op: 26, A: 80}, {Op: 11, A: 2}, {Op: 30, A: 10, Flags: 1}, {Op: 93}, {Op: 1, A: 11}, {Op: 92, A: 3}, {Op: 30, A: 2, B: 2, Flags: 2}, {Op: 26, A: 86}, {Op: 2}, {Op: 9}, {Op: 11, A: 2}, {Op: 12, A: 1}, {Op: 9}, {Op: 5}, {Op: 30, A: 12, C: 1}, {Op: 9}, {Op: 11, A: 1}, {Op: 53}}, Consts: []object.Value{ object.IntValue(1), - object.NewString(""), object.FString("wrong number of arguments (given ", ""), object.FString(", expected 0..1)", ""), object.IntValue(0), object.Symbol("to_hash"), - object.NewString(""), object.FString("can't convert ", ""), object.FString(" to Hash (", ""), object.FString("#to_hash gives ", ""), object.FString(")", ""), - object.NewString(""), object.FString("no implicit conversion of ", ""), object.FString("nil", ""), object.FString(" into Hash", ""), }, - Names: []string{"length", "ArgumentError", "to_s", "raise", "empty?", "[]", "Hash", "is_a?", "respond_to?", "to_hash", "TypeError", "class", "nil?", "__each_packed"}, + Names: []string{"length", "ArgumentError", "raise", "empty?", "[]", "Hash", "is_a?", "respond_to?", "to_hash", "TypeError", "class", "nil?", "__each_packed"}, Params: []string{"args"}, NumRequired: 0, SplatIndex: 0, @@ -1850,23 +1833,22 @@ func embeddedPrelude() *bytecode.ISeq { BlockSlot: -1, NumLocals: 3, Locals: []string{"args", "h", "a"}, - Lines: []bytecode.LineEntry{{PC: 0, Line: 598}, {PC: 22, Line: 599}, {PC: 25, Line: 600}, {PC: 28, Line: 602}, {PC: 33, Line: 603}, {PC: 38, Line: 604}, {PC: 42, Line: 605}, {PC: 46, Line: 606}, {PC: 86, Line: 608}, {PC: 108, Line: 611}, {PC: 111, Line: 613}, {PC: 114, Line: 625}}, + Lines: []bytecode.LineEntry{{PC: 0, Line: 598}, {PC: 17, Line: 599}, {PC: 20, Line: 600}, {PC: 23, Line: 602}, {PC: 28, Line: 603}, {PC: 33, Line: 604}, {PC: 37, Line: 605}, {PC: 41, Line: 606}, {PC: 70, Line: 608}, {PC: 87, Line: 611}, {PC: 90, Line: 613}, {PC: 93, Line: 625}}, FirstLine: 597, Children: []*bytecode.ISeq{ &bytecode.ISeq{ Name: "block in tally", - Insns: []bytecode.Instr{{Op: 11, A: 1, B: 1}, {Op: 11}, {Op: 30, B: 1, Flags: 3}, {Op: 28, A: 51}, {Op: 11, A: 1, B: 1}, {Op: 11}, {Op: 30, A: 1, B: 1, Flags: 3}, {Op: 12, A: 1}, {Op: 9}, {Op: 11, A: 1}, {Op: 33, A: 2}, {Op: 30, A: 3, B: 1, Flags: 3}, {Op: 25}, {Op: 28, A: 49}, {Op: 11, A: 1}, {Op: 30, A: 4, Flags: 1}, {Op: 28, A: 19}, {Op: 1}, {Op: 26, A: 33}, {Op: 11, A: 1}, {Op: 3}, {Op: 22}, {Op: 28, A: 25}, {Op: 1, A: 1}, {Op: 26, A: 33}, {Op: 11, A: 1}, {Op: 4}, {Op: 22}, {Op: 28, A: 31}, {Op: 1, A: 2}, {Op: 26, A: 33}, {Op: 11, A: 1}, {Op: 30, A: 5, Flags: 1}, {Op: 12, A: 2}, {Op: 9}, {Op: 5}, {Op: 33, A: 6}, {Op: 1, A: 3}, {Op: 1, A: 4}, {Op: 30, A: 7}, {Op: 13}, {Op: 11, A: 2}, {Op: 30, A: 7}, {Op: 13}, {Op: 1, A: 5}, {Op: 30, A: 7}, {Op: 13}, {Op: 30, A: 8, B: 2, Flags: 2}, {Op: 26, A: 50}, {Op: 2}, {Op: 26, A: 53}, {Op: 1, A: 6}, {Op: 12, A: 1}, {Op: 9}, {Op: 11, A: 1, B: 1}, {Op: 11}, {Op: 11, A: 1}, {Op: 1, A: 7}, {Op: 13}, {Op: 12, A: 3}, {Op: 30, A: 9, B: 2, Flags: 3}, {Op: 9}, {Op: 11, A: 3}, {Op: 53}}, + Insns: []bytecode.Instr{{Op: 11, A: 1, B: 1}, {Op: 11}, {Op: 30, B: 1, Flags: 3}, {Op: 28, A: 46}, {Op: 11, A: 1, B: 1}, {Op: 11}, {Op: 30, A: 1, B: 1, Flags: 3}, {Op: 12, A: 1}, {Op: 9}, {Op: 11, A: 1}, {Op: 33, A: 2}, {Op: 30, A: 3, B: 1, Flags: 3}, {Op: 25}, {Op: 28, A: 44}, {Op: 11, A: 1}, {Op: 30, A: 4, Flags: 1}, {Op: 28, A: 19}, {Op: 1}, {Op: 26, A: 33}, {Op: 11, A: 1}, {Op: 3}, {Op: 22}, {Op: 28, A: 25}, {Op: 1, A: 1}, {Op: 26, A: 33}, {Op: 11, A: 1}, {Op: 4}, {Op: 22}, {Op: 28, A: 31}, {Op: 1, A: 2}, {Op: 26, A: 33}, {Op: 11, A: 1}, {Op: 30, A: 5, Flags: 1}, {Op: 12, A: 2}, {Op: 9}, {Op: 5}, {Op: 33, A: 6}, {Op: 1, A: 3}, {Op: 11, A: 2}, {Op: 93}, {Op: 1, A: 4}, {Op: 92, A: 3}, {Op: 30, A: 7, B: 2, Flags: 2}, {Op: 26, A: 45}, {Op: 2}, {Op: 26, A: 48}, {Op: 1, A: 5}, {Op: 12, A: 1}, {Op: 9}, {Op: 11, A: 1, B: 1}, {Op: 11}, {Op: 11, A: 1}, {Op: 1, A: 6}, {Op: 13}, {Op: 12, A: 3}, {Op: 30, A: 8, B: 2, Flags: 3}, {Op: 9}, {Op: 11, A: 3}, {Op: 53}}, Consts: []object.Value{ object.FString("nil", ""), object.FString("true", ""), object.FString("false", ""), - object.NewString(""), object.FString("wrong argument type ", ""), object.FString(" (expected Integer)", ""), object.IntValue(0), object.IntValue(1), }, - Names: []string{"key?", "[]", "Integer", "is_a?", "nil?", "class", "TypeError", "to_s", "raise", "[]="}, + Names: []string{"key?", "[]", "Integer", "is_a?", "nil?", "class", "TypeError", "raise", "[]="}, Params: []string{"x"}, NumRequired: 1, SplatIndex: -1, @@ -1875,7 +1857,7 @@ func embeddedPrelude() *bytecode.ISeq { BlockSlot: -1, NumLocals: 4, Locals: []string{"x", "c", "tn", ""}, - Lines: []bytecode.LineEntry{{PC: 0, Line: 614}, {PC: 4, Line: 615}, {PC: 9, Line: 616}, {PC: 14, Line: 617}, {PC: 35, Line: 618}, {PC: 51, Line: 621}, {PC: 54, Line: 623}}, + Lines: []bytecode.LineEntry{{PC: 0, Line: 614}, {PC: 4, Line: 615}, {PC: 9, Line: 616}, {PC: 14, Line: 617}, {PC: 35, Line: 618}, {PC: 46, Line: 621}, {PC: 49, Line: 623}}, FirstLine: 613, Super: "", }, @@ -1902,15 +1884,14 @@ func embeddedPrelude() *bytecode.ISeq { Children: []*bytecode.ISeq{ &bytecode.ISeq{ Name: "block in zip", - Insns: []bytecode.Instr{{Op: 11}, {Op: 1}, {Op: 30, B: 1, Flags: 3}, {Op: 28, A: 7}, {Op: 11}, {Op: 30, A: 1, Flags: 1}, {Op: 26, A: 30}, {Op: 11}, {Op: 1, A: 1}, {Op: 30, B: 1, Flags: 3}, {Op: 28, A: 16}, {Op: 11}, {Op: 1, A: 1}, {Op: 30, A: 2, B: 1, Flags: 3}, {Op: 30, A: 3, Flags: 1}, {Op: 26, A: 30}, {Op: 5}, {Op: 33, A: 4}, {Op: 1, A: 2}, {Op: 1, A: 3}, {Op: 30, A: 5}, {Op: 13}, {Op: 11}, {Op: 30, A: 6, Flags: 1}, {Op: 30, A: 5}, {Op: 13}, {Op: 1, A: 4}, {Op: 30, A: 5}, {Op: 13}, {Op: 30, A: 7, B: 2, Flags: 2}, {Op: 53}}, + Insns: []bytecode.Instr{{Op: 11}, {Op: 1}, {Op: 30, B: 1, Flags: 3}, {Op: 28, A: 7}, {Op: 11}, {Op: 30, A: 1, Flags: 1}, {Op: 26, A: 25}, {Op: 11}, {Op: 1, A: 1}, {Op: 30, B: 1, Flags: 3}, {Op: 28, A: 16}, {Op: 11}, {Op: 1, A: 1}, {Op: 30, A: 2, B: 1, Flags: 3}, {Op: 30, A: 3, Flags: 1}, {Op: 26, A: 25}, {Op: 5}, {Op: 33, A: 4}, {Op: 1, A: 2}, {Op: 11}, {Op: 30, A: 5, Flags: 1}, {Op: 93}, {Op: 1, A: 3}, {Op: 92, A: 3}, {Op: 30, A: 6, B: 2, Flags: 2}, {Op: 53}}, Consts: []object.Value{ object.Symbol("to_ary"), object.Symbol("each"), - object.NewString(""), object.FString("wrong argument type ", ""), object.FString(" (must respond to :each)", ""), }, - Names: []string{"respond_to?", "to_ary", "to_enum", "to_a", "TypeError", "to_s", "class", "raise"}, + Names: []string{"respond_to?", "to_ary", "to_enum", "to_a", "TypeError", "class", "raise"}, Params: []string{"o"}, NumRequired: 1, SplatIndex: -1, @@ -1965,10 +1946,9 @@ func embeddedPrelude() *bytecode.ISeq { }, &bytecode.ISeq{ Name: "find_index", - Insns: []bytecode.Instr{{Op: 11}, {Op: 30, Flags: 1}, {Op: 1}, {Op: 19}, {Op: 28, A: 20}, {Op: 5}, {Op: 33, A: 1}, {Op: 1, A: 1}, {Op: 1, A: 2}, {Op: 30, A: 2}, {Op: 13}, {Op: 11}, {Op: 30, Flags: 1}, {Op: 30, A: 2}, {Op: 13}, {Op: 1, A: 3}, {Op: 30, A: 2}, {Op: 13}, {Op: 30, A: 3, B: 2, Flags: 2}, {Op: 26, A: 21}, {Op: 2}, {Op: 9}, {Op: 11}, {Op: 30, A: 4, Flags: 1}, {Op: 10}, {Op: 28, A: 29}, {Op: 9}, {Op: 52}, {Op: 25}, {Op: 28, A: 35}, {Op: 5}, {Op: 1, A: 4}, {Op: 30, A: 5, B: 1, C: 1, Flags: 2}, {Op: 53}, {Op: 26, A: 36}, {Op: 2}, {Op: 9}, {Op: 11}, {Op: 30, A: 4, Flags: 1}, {Op: 25}, {Op: 10}, {Op: 28, A: 44}, {Op: 9}, {Op: 52}, {Op: 28, A: 52}, {Op: 5}, {Op: 1, A: 5}, {Op: 1, A: 6}, {Op: 1}, {Op: 7, A: 1}, {Op: 30, A: 6, B: 2}, {Op: 26, A: 53}, {Op: 2}, {Op: 9}, {Op: 2}, {Op: 12, A: 1}, {Op: 9}, {Op: 1, A: 7}, {Op: 12, A: 2}, {Op: 9}, {Op: 5}, {Op: 30, A: 7, C: 2}, {Op: 9}, {Op: 11, A: 1}, {Op: 53}}, + Insns: []bytecode.Instr{{Op: 11}, {Op: 30, Flags: 1}, {Op: 1}, {Op: 19}, {Op: 28, A: 15}, {Op: 5}, {Op: 33, A: 1}, {Op: 1, A: 1}, {Op: 11}, {Op: 30, Flags: 1}, {Op: 93}, {Op: 1, A: 2}, {Op: 92, A: 3}, {Op: 30, A: 2, B: 2, Flags: 2}, {Op: 26, A: 16}, {Op: 2}, {Op: 9}, {Op: 11}, {Op: 30, A: 3, Flags: 1}, {Op: 10}, {Op: 28, A: 24}, {Op: 9}, {Op: 52}, {Op: 25}, {Op: 28, A: 30}, {Op: 5}, {Op: 1, A: 3}, {Op: 30, A: 4, B: 1, C: 1, Flags: 2}, {Op: 53}, {Op: 26, A: 31}, {Op: 2}, {Op: 9}, {Op: 11}, {Op: 30, A: 3, Flags: 1}, {Op: 25}, {Op: 10}, {Op: 28, A: 39}, {Op: 9}, {Op: 52}, {Op: 28, A: 47}, {Op: 5}, {Op: 1, A: 4}, {Op: 1, A: 5}, {Op: 1}, {Op: 7, A: 1}, {Op: 30, A: 5, B: 2}, {Op: 26, A: 48}, {Op: 2}, {Op: 9}, {Op: 2}, {Op: 12, A: 1}, {Op: 9}, {Op: 1, A: 6}, {Op: 12, A: 2}, {Op: 9}, {Op: 5}, {Op: 30, A: 6, C: 2}, {Op: 9}, {Op: 11, A: 1}, {Op: 53}}, Consts: []object.Value{ object.IntValue(1), - object.NewString(""), object.FString("wrong number of arguments (given ", ""), object.FString(", expected 0..1)", ""), object.Symbol("find_index"), @@ -1976,7 +1956,7 @@ func embeddedPrelude() *bytecode.ISeq { object.Symbol("uplevel"), object.IntValue(0), }, - Names: []string{"length", "ArgumentError", "to_s", "raise", "empty?", "enum_for", "warn", "each"}, + Names: []string{"length", "ArgumentError", "raise", "empty?", "enum_for", "warn", "each"}, Params: []string{"args"}, NumRequired: 0, SplatIndex: 0, @@ -1985,7 +1965,7 @@ func embeddedPrelude() *bytecode.ISeq { BlockSlot: -1, NumLocals: 3, Locals: []string{"args", "idx", "i"}, - Lines: []bytecode.LineEntry{{PC: 0, Line: 663}, {PC: 22, Line: 664}, {PC: 37, Line: 666}, {PC: 54, Line: 259}, {PC: 57, Line: 668}, {PC: 60, Line: 669}, {PC: 63, Line: 673}}, + Lines: []bytecode.LineEntry{{PC: 0, Line: 663}, {PC: 17, Line: 664}, {PC: 32, Line: 666}, {PC: 49, Line: 259}, {PC: 52, Line: 668}, {PC: 55, Line: 669}, {PC: 58, Line: 673}}, FirstLine: 662, Children: []*bytecode.ISeq{ &bytecode.ISeq{ @@ -2309,17 +2289,16 @@ func embeddedPrelude() *bytecode.ISeq { }, &bytecode.ISeq{ Name: "first", - Insns: []bytecode.Instr{{Op: 11}, {Op: 30, Flags: 1}, {Op: 1}, {Op: 19}, {Op: 28, A: 20}, {Op: 5}, {Op: 33, A: 1}, {Op: 1, A: 1}, {Op: 1, A: 2}, {Op: 30, A: 2}, {Op: 13}, {Op: 11}, {Op: 30, Flags: 1}, {Op: 30, A: 2}, {Op: 13}, {Op: 1, A: 3}, {Op: 30, A: 2}, {Op: 13}, {Op: 30, A: 3, B: 2, Flags: 2}, {Op: 26, A: 21}, {Op: 2}, {Op: 9}, {Op: 11}, {Op: 30, A: 4, Flags: 1}, {Op: 28, A: 35}, {Op: 2}, {Op: 12, A: 1}, {Op: 9}, {Op: 5}, {Op: 1, A: 4}, {Op: 30, A: 5, B: 1, C: 1, Flags: 2}, {Op: 9}, {Op: 11, A: 1}, {Op: 53}, {Op: 26, A: 36}, {Op: 2}, {Op: 9}, {Op: 5}, {Op: 11}, {Op: 1, A: 5}, {Op: 30, A: 6, B: 1, Flags: 3}, {Op: 30, A: 7, B: 1, Flags: 2}, {Op: 12, A: 2}, {Op: 9}, {Op: 11, A: 2}, {Op: 1, A: 5}, {Op: 18}, {Op: 28, A: 53}, {Op: 5}, {Op: 33, A: 1}, {Op: 1, A: 6}, {Op: 30, A: 3, B: 2, Flags: 2}, {Op: 26, A: 54}, {Op: 2}, {Op: 9}, {Op: 11, A: 2}, {Op: 1, A: 5}, {Op: 22}, {Op: 28, A: 62}, {Op: 6}, {Op: 53}, {Op: 26, A: 63}, {Op: 2}, {Op: 9}, {Op: 6}, {Op: 12, A: 3}, {Op: 9}, {Op: 5}, {Op: 1, A: 4}, {Op: 30, A: 5, B: 1, C: 2, Flags: 2}, {Op: 9}, {Op: 11, A: 3}, {Op: 53}}, + Insns: []bytecode.Instr{{Op: 11}, {Op: 30, Flags: 1}, {Op: 1}, {Op: 19}, {Op: 28, A: 15}, {Op: 5}, {Op: 33, A: 1}, {Op: 1, A: 1}, {Op: 11}, {Op: 30, Flags: 1}, {Op: 93}, {Op: 1, A: 2}, {Op: 92, A: 3}, {Op: 30, A: 2, B: 2, Flags: 2}, {Op: 26, A: 16}, {Op: 2}, {Op: 9}, {Op: 11}, {Op: 30, A: 3, Flags: 1}, {Op: 28, A: 30}, {Op: 2}, {Op: 12, A: 1}, {Op: 9}, {Op: 5}, {Op: 1, A: 3}, {Op: 30, A: 4, B: 1, C: 1, Flags: 2}, {Op: 9}, {Op: 11, A: 1}, {Op: 53}, {Op: 26, A: 31}, {Op: 2}, {Op: 9}, {Op: 5}, {Op: 11}, {Op: 1, A: 4}, {Op: 30, A: 5, B: 1, Flags: 3}, {Op: 30, A: 6, B: 1, Flags: 2}, {Op: 12, A: 2}, {Op: 9}, {Op: 11, A: 2}, {Op: 1, A: 4}, {Op: 18}, {Op: 28, A: 48}, {Op: 5}, {Op: 33, A: 1}, {Op: 1, A: 5}, {Op: 30, A: 2, B: 2, Flags: 2}, {Op: 26, A: 49}, {Op: 2}, {Op: 9}, {Op: 11, A: 2}, {Op: 1, A: 4}, {Op: 22}, {Op: 28, A: 57}, {Op: 6}, {Op: 53}, {Op: 26, A: 58}, {Op: 2}, {Op: 9}, {Op: 6}, {Op: 12, A: 3}, {Op: 9}, {Op: 5}, {Op: 1, A: 3}, {Op: 30, A: 4, B: 1, C: 2, Flags: 2}, {Op: 9}, {Op: 11, A: 3}, {Op: 53}}, Consts: []object.Value{ object.IntValue(1), - object.NewString(""), object.FString("wrong number of arguments (given ", ""), object.FString(", expected 0..1)", ""), object.Symbol("__enum_first"), object.IntValue(0), object.FString("attempt to take negative size", ""), }, - Names: []string{"length", "ArgumentError", "to_s", "raise", "empty?", "catch", "[]", "__enum_int_arg"}, + Names: []string{"length", "ArgumentError", "raise", "empty?", "catch", "[]", "__enum_int_arg"}, Params: []string{"args"}, NumRequired: 0, SplatIndex: 0, @@ -2328,7 +2307,7 @@ func embeddedPrelude() *bytecode.ISeq { BlockSlot: -1, NumLocals: 4, Locals: []string{"args", "result", "n", "r"}, - Lines: []bytecode.LineEntry{{PC: 0, Line: 769}, {PC: 22, Line: 770}, {PC: 25, Line: 259}, {PC: 28, Line: 772}, {PC: 32, Line: 775}, {PC: 37, Line: 777}, {PC: 44, Line: 778}, {PC: 55, Line: 779}, {PC: 64, Line: 780}, {PC: 67, Line: 781}, {PC: 71, Line: 787}}, + Lines: []bytecode.LineEntry{{PC: 0, Line: 769}, {PC: 17, Line: 770}, {PC: 20, Line: 259}, {PC: 23, Line: 772}, {PC: 27, Line: 775}, {PC: 32, Line: 777}, {PC: 39, Line: 778}, {PC: 50, Line: 779}, {PC: 59, Line: 780}, {PC: 62, Line: 781}, {PC: 66, Line: 787}}, FirstLine: 768, Children: []*bytecode.ISeq{ &bytecode.ISeq{ @@ -2619,10 +2598,9 @@ func embeddedPrelude() *bytecode.ISeq { }, &bytecode.ISeq{ Name: "slice_before", - Insns: []bytecode.Instr{{Op: 11}, {Op: 30, Flags: 1}, {Op: 1}, {Op: 19}, {Op: 28, A: 20}, {Op: 5}, {Op: 33, A: 1}, {Op: 1, A: 1}, {Op: 1, A: 2}, {Op: 30, A: 2}, {Op: 13}, {Op: 11}, {Op: 30, Flags: 1}, {Op: 30, A: 2}, {Op: 13}, {Op: 1, A: 3}, {Op: 30, A: 2}, {Op: 13}, {Op: 30, A: 3, B: 2, Flags: 2}, {Op: 26, A: 21}, {Op: 2}, {Op: 9}, {Op: 52}, {Op: 28, A: 35}, {Op: 11}, {Op: 30, A: 4, Flags: 1}, {Op: 25}, {Op: 28, A: 33}, {Op: 5}, {Op: 33, A: 1}, {Op: 1, A: 4}, {Op: 30, A: 3, B: 2, Flags: 2}, {Op: 26, A: 34}, {Op: 2}, {Op: 26, A: 49}, {Op: 11}, {Op: 30, A: 4, Flags: 1}, {Op: 28, A: 43}, {Op: 5}, {Op: 33, A: 1}, {Op: 1, A: 5}, {Op: 30, A: 3, B: 2, Flags: 2}, {Op: 26, A: 44}, {Op: 2}, {Op: 9}, {Op: 11}, {Op: 1, A: 6}, {Op: 30, A: 5, B: 1, Flags: 3}, {Op: 12, A: 1}, {Op: 9}, {Op: 6}, {Op: 12, A: 2}, {Op: 9}, {Op: 2}, {Op: 12, A: 3}, {Op: 9}, {Op: 5}, {Op: 30, A: 6, C: 1}, {Op: 9}, {Op: 11, A: 3}, {Op: 30, A: 7, Flags: 1}, {Op: 25}, {Op: 28, A: 67}, {Op: 11, A: 2}, {Op: 11, A: 3}, {Op: 30, A: 8, B: 1}, {Op: 26, A: 68}, {Op: 2}, {Op: 9}, {Op: 11, A: 2}, {Op: 1, A: 7}, {Op: 30, A: 9, B: 1, C: 2, Flags: 3}, {Op: 53}}, + Insns: []bytecode.Instr{{Op: 11}, {Op: 30, Flags: 1}, {Op: 1}, {Op: 19}, {Op: 28, A: 15}, {Op: 5}, {Op: 33, A: 1}, {Op: 1, A: 1}, {Op: 11}, {Op: 30, Flags: 1}, {Op: 93}, {Op: 1, A: 2}, {Op: 92, A: 3}, {Op: 30, A: 2, B: 2, Flags: 2}, {Op: 26, A: 16}, {Op: 2}, {Op: 9}, {Op: 52}, {Op: 28, A: 30}, {Op: 11}, {Op: 30, A: 3, Flags: 1}, {Op: 25}, {Op: 28, A: 28}, {Op: 5}, {Op: 33, A: 1}, {Op: 1, A: 3}, {Op: 30, A: 2, B: 2, Flags: 2}, {Op: 26, A: 29}, {Op: 2}, {Op: 26, A: 44}, {Op: 11}, {Op: 30, A: 3, Flags: 1}, {Op: 28, A: 38}, {Op: 5}, {Op: 33, A: 1}, {Op: 1, A: 4}, {Op: 30, A: 2, B: 2, Flags: 2}, {Op: 26, A: 39}, {Op: 2}, {Op: 9}, {Op: 11}, {Op: 1, A: 5}, {Op: 30, A: 4, B: 1, Flags: 3}, {Op: 12, A: 1}, {Op: 9}, {Op: 6}, {Op: 12, A: 2}, {Op: 9}, {Op: 2}, {Op: 12, A: 3}, {Op: 9}, {Op: 5}, {Op: 30, A: 5, C: 1}, {Op: 9}, {Op: 11, A: 3}, {Op: 30, A: 6, Flags: 1}, {Op: 25}, {Op: 28, A: 62}, {Op: 11, A: 2}, {Op: 11, A: 3}, {Op: 30, A: 7, B: 1}, {Op: 26, A: 63}, {Op: 2}, {Op: 9}, {Op: 11, A: 2}, {Op: 1, A: 6}, {Op: 30, A: 8, B: 1, C: 2, Flags: 3}, {Op: 53}}, Consts: []object.Value{ object.IntValue(1), - object.NewString(""), object.FString("wrong number of arguments (given ", ""), object.FString(", expected 1)", ""), object.FString("both pattern and block are given", ""), @@ -2630,7 +2608,7 @@ func embeddedPrelude() *bytecode.ISeq { object.IntValue(0), object.Symbol("each"), }, - Names: []string{"length", "ArgumentError", "to_s", "raise", "empty?", "[]", "__each_packed", "nil?", "<<", "to_enum"}, + Names: []string{"length", "ArgumentError", "raise", "empty?", "[]", "__each_packed", "nil?", "<<", "to_enum"}, Params: []string{"args"}, NumRequired: 0, SplatIndex: 0, @@ -2639,7 +2617,7 @@ func embeddedPrelude() *bytecode.ISeq { BlockSlot: -1, NumLocals: 4, Locals: []string{"args", "pat", "runs", "cur"}, - Lines: []bytecode.LineEntry{{PC: 0, Line: 901}, {PC: 22, Line: 902}, {PC: 24, Line: 903}, {PC: 35, Line: 905}, {PC: 45, Line: 906}, {PC: 50, Line: 908}, {PC: 53, Line: 259}, {PC: 56, Line: 910}, {PC: 59, Line: 919}, {PC: 69, Line: 920}}, + Lines: []bytecode.LineEntry{{PC: 0, Line: 901}, {PC: 17, Line: 902}, {PC: 19, Line: 903}, {PC: 30, Line: 905}, {PC: 40, Line: 906}, {PC: 45, Line: 908}, {PC: 48, Line: 259}, {PC: 51, Line: 910}, {PC: 54, Line: 919}, {PC: 64, Line: 920}}, FirstLine: 900, Children: []*bytecode.ISeq{ &bytecode.ISeq{ @@ -2676,10 +2654,9 @@ func embeddedPrelude() *bytecode.ISeq { }, &bytecode.ISeq{ Name: "slice_after", - Insns: []bytecode.Instr{{Op: 11}, {Op: 30, Flags: 1}, {Op: 1}, {Op: 19}, {Op: 28, A: 20}, {Op: 5}, {Op: 33, A: 1}, {Op: 1, A: 1}, {Op: 1, A: 2}, {Op: 30, A: 2}, {Op: 13}, {Op: 11}, {Op: 30, Flags: 1}, {Op: 30, A: 2}, {Op: 13}, {Op: 1, A: 3}, {Op: 30, A: 2}, {Op: 13}, {Op: 30, A: 3, B: 2, Flags: 2}, {Op: 26, A: 21}, {Op: 2}, {Op: 9}, {Op: 52}, {Op: 28, A: 35}, {Op: 11}, {Op: 30, A: 4, Flags: 1}, {Op: 25}, {Op: 28, A: 33}, {Op: 5}, {Op: 33, A: 1}, {Op: 1, A: 4}, {Op: 30, A: 3, B: 2, Flags: 2}, {Op: 26, A: 34}, {Op: 2}, {Op: 26, A: 49}, {Op: 11}, {Op: 30, A: 4, Flags: 1}, {Op: 28, A: 43}, {Op: 5}, {Op: 33, A: 1}, {Op: 1, A: 5}, {Op: 30, A: 3, B: 2, Flags: 2}, {Op: 26, A: 44}, {Op: 2}, {Op: 9}, {Op: 11}, {Op: 1, A: 6}, {Op: 30, A: 5, B: 1, Flags: 3}, {Op: 12, A: 1}, {Op: 9}, {Op: 6}, {Op: 12, A: 2}, {Op: 9}, {Op: 6}, {Op: 12, A: 3}, {Op: 9}, {Op: 5}, {Op: 30, A: 6, C: 1}, {Op: 9}, {Op: 11, A: 3}, {Op: 30, A: 4, Flags: 1}, {Op: 25}, {Op: 28, A: 67}, {Op: 11, A: 2}, {Op: 11, A: 3}, {Op: 30, A: 7, B: 1}, {Op: 26, A: 68}, {Op: 2}, {Op: 9}, {Op: 11, A: 2}, {Op: 1, A: 7}, {Op: 30, A: 8, B: 1, C: 2, Flags: 3}, {Op: 53}}, + Insns: []bytecode.Instr{{Op: 11}, {Op: 30, Flags: 1}, {Op: 1}, {Op: 19}, {Op: 28, A: 15}, {Op: 5}, {Op: 33, A: 1}, {Op: 1, A: 1}, {Op: 11}, {Op: 30, Flags: 1}, {Op: 93}, {Op: 1, A: 2}, {Op: 92, A: 3}, {Op: 30, A: 2, B: 2, Flags: 2}, {Op: 26, A: 16}, {Op: 2}, {Op: 9}, {Op: 52}, {Op: 28, A: 30}, {Op: 11}, {Op: 30, A: 3, Flags: 1}, {Op: 25}, {Op: 28, A: 28}, {Op: 5}, {Op: 33, A: 1}, {Op: 1, A: 3}, {Op: 30, A: 2, B: 2, Flags: 2}, {Op: 26, A: 29}, {Op: 2}, {Op: 26, A: 44}, {Op: 11}, {Op: 30, A: 3, Flags: 1}, {Op: 28, A: 38}, {Op: 5}, {Op: 33, A: 1}, {Op: 1, A: 4}, {Op: 30, A: 2, B: 2, Flags: 2}, {Op: 26, A: 39}, {Op: 2}, {Op: 9}, {Op: 11}, {Op: 1, A: 5}, {Op: 30, A: 4, B: 1, Flags: 3}, {Op: 12, A: 1}, {Op: 9}, {Op: 6}, {Op: 12, A: 2}, {Op: 9}, {Op: 6}, {Op: 12, A: 3}, {Op: 9}, {Op: 5}, {Op: 30, A: 5, C: 1}, {Op: 9}, {Op: 11, A: 3}, {Op: 30, A: 3, Flags: 1}, {Op: 25}, {Op: 28, A: 62}, {Op: 11, A: 2}, {Op: 11, A: 3}, {Op: 30, A: 6, B: 1}, {Op: 26, A: 63}, {Op: 2}, {Op: 9}, {Op: 11, A: 2}, {Op: 1, A: 6}, {Op: 30, A: 7, B: 1, C: 2, Flags: 3}, {Op: 53}}, Consts: []object.Value{ object.IntValue(1), - object.NewString(""), object.FString("wrong number of arguments (given ", ""), object.FString(", expected 1)", ""), object.FString("both pattern and block are given", ""), @@ -2687,7 +2664,7 @@ func embeddedPrelude() *bytecode.ISeq { object.IntValue(0), object.Symbol("each"), }, - Names: []string{"length", "ArgumentError", "to_s", "raise", "empty?", "[]", "__each_packed", "<<", "to_enum"}, + Names: []string{"length", "ArgumentError", "raise", "empty?", "[]", "__each_packed", "<<", "to_enum"}, Params: []string{"args"}, NumRequired: 0, SplatIndex: 0, @@ -2696,7 +2673,7 @@ func embeddedPrelude() *bytecode.ISeq { BlockSlot: -1, NumLocals: 4, Locals: []string{"args", "pat", "runs", "cur"}, - Lines: []bytecode.LineEntry{{PC: 0, Line: 924}, {PC: 22, Line: 925}, {PC: 24, Line: 926}, {PC: 35, Line: 928}, {PC: 45, Line: 929}, {PC: 50, Line: 931}, {PC: 53, Line: 932}, {PC: 56, Line: 933}, {PC: 59, Line: 940}, {PC: 69, Line: 941}}, + Lines: []bytecode.LineEntry{{PC: 0, Line: 924}, {PC: 17, Line: 925}, {PC: 19, Line: 926}, {PC: 30, Line: 928}, {PC: 40, Line: 929}, {PC: 45, Line: 931}, {PC: 48, Line: 932}, {PC: 51, Line: 933}, {PC: 54, Line: 940}, {PC: 64, Line: 941}}, FirstLine: 923, Children: []*bytecode.ISeq{ &bytecode.ISeq{ @@ -4613,16 +4590,15 @@ func embeddedPrelude() *bytecode.ISeq { }, &bytecode.ISeq{ Name: "initialize", - Insns: []bytecode.Instr{{Op: 5}, {Op: 30}, {Op: 11}, {Op: 30, A: 1, B: 1, Flags: 3}, {Op: 25}, {Op: 28, A: 20}, {Op: 5}, {Op: 33, A: 2}, {Op: 1}, {Op: 1, A: 1}, {Op: 30, A: 3}, {Op: 13}, {Op: 11}, {Op: 30, A: 3}, {Op: 13}, {Op: 1, A: 2}, {Op: 30, A: 3}, {Op: 13}, {Op: 30, A: 4, B: 2, Flags: 2}, {Op: 26, A: 21}, {Op: 2}, {Op: 9}, {Op: 11}, {Op: 30, A: 3, Flags: 1}, {Op: 30, A: 5, Flags: 1}, {Op: 1, A: 3}, {Op: 1, A: 4}, {Op: 30, A: 6, B: 2, Flags: 3}, {Op: 32, A: 7}, {Op: 9}, {Op: 31, A: 7}, {Op: 30, A: 8, Flags: 1}, {Op: 28, A: 36}, {Op: 1, A: 5}, {Op: 32, A: 7}, {Op: 26, A: 37}, {Op: 2}, {Op: 53}}, + Insns: []bytecode.Instr{{Op: 5}, {Op: 30}, {Op: 11}, {Op: 30, A: 1, B: 1, Flags: 3}, {Op: 25}, {Op: 28, A: 15}, {Op: 5}, {Op: 33, A: 2}, {Op: 1}, {Op: 11}, {Op: 93}, {Op: 1, A: 1}, {Op: 92, A: 3}, {Op: 30, A: 3, B: 2, Flags: 2}, {Op: 26, A: 16}, {Op: 2}, {Op: 9}, {Op: 11}, {Op: 30, A: 4, Flags: 1}, {Op: 30, A: 5, Flags: 1}, {Op: 1, A: 2}, {Op: 1, A: 3}, {Op: 30, A: 6, B: 2, Flags: 3}, {Op: 32, A: 7}, {Op: 9}, {Op: 31, A: 7}, {Op: 30, A: 8, Flags: 1}, {Op: 28, A: 31}, {Op: 1, A: 4}, {Op: 32, A: 7}, {Op: 26, A: 32}, {Op: 2}, {Op: 53}}, Consts: []object.Value{ - object.NewString(""), object.FString("Malformed version number string ", ""), object.FString("", ""), object.FString("-", ""), object.FString(".pre.", ""), object.FString("0", ""), }, - Names: []string{"class", "correct?", "ArgumentError", "to_s", "raise", "strip", "gsub", "@version", "empty?"}, + Names: []string{"class", "correct?", "ArgumentError", "raise", "to_s", "strip", "gsub", "@version", "empty?"}, Params: []string{"version"}, NumRequired: 1, SplatIndex: -1, @@ -4631,7 +4607,7 @@ func embeddedPrelude() *bytecode.ISeq { BlockSlot: -1, NumLocals: 1, Locals: []string{"version"}, - Lines: []bytecode.LineEntry{{PC: 0, Line: 109}, {PC: 6, Line: 1510}, {PC: 22, Line: 1512}, {PC: 30, Line: 1513}}, + Lines: []bytecode.LineEntry{{PC: 0, Line: 109}, {PC: 6, Line: 1510}, {PC: 17, Line: 1512}, {PC: 25, Line: 1513}}, FirstLine: 1508, Super: "", }, @@ -4651,21 +4627,20 @@ func embeddedPrelude() *bytecode.ISeq { }, &bytecode.ISeq{ Name: "inspect", - Insns: []bytecode.Instr{{Op: 1}, {Op: 1, A: 1}, {Op: 30}, {Op: 13}, {Op: 5}, {Op: 30, A: 1}, {Op: 30, A: 2, Flags: 1}, {Op: 30}, {Op: 13}, {Op: 1, A: 2}, {Op: 30}, {Op: 13}, {Op: 31, A: 3}, {Op: 30, A: 4, Flags: 1}, {Op: 30}, {Op: 13}, {Op: 1, A: 3}, {Op: 30}, {Op: 13}, {Op: 53}}, + Insns: []bytecode.Instr{{Op: 1}, {Op: 5}, {Op: 30}, {Op: 30, A: 1, Flags: 1}, {Op: 93}, {Op: 1, A: 1}, {Op: 31, A: 2}, {Op: 30, A: 3, Flags: 1}, {Op: 93}, {Op: 1, A: 2}, {Op: 92, A: 5}, {Op: 53}}, Consts: []object.Value{ - object.NewString(""), object.FString("#<", ""), object.FString(" ", ""), object.FString(">", ""), }, - Names: []string{"to_s", "class", "name", "@version", "inspect"}, + Names: []string{"class", "name", "@version", "inspect"}, NumRequired: 0, SplatIndex: -1, PostCount: 0, KwRestSlot: -1, BlockSlot: -1, NumLocals: 0, - Lines: []bytecode.LineEntry{{PC: 0, Line: 1521}, {PC: 4, Line: 109}, {PC: 12, Line: 1521}}, + Lines: []bytecode.LineEntry{{PC: 0, Line: 1521}, {PC: 1, Line: 109}, {PC: 6, Line: 1521}}, FirstLine: 1520, Super: "", }, @@ -5048,10 +5023,9 @@ func embeddedPrelude() *bytecode.ISeq { }, &bytecode.ISeq{ Name: "parse", - Insns: []bytecode.Instr{{Op: 11}, {Op: 33}, {Op: 30, A: 1, B: 1, Flags: 3}, {Op: 28, A: 9}, {Op: 1}, {Op: 11}, {Op: 6, A: 2}, {Op: 53}, {Op: 26, A: 10}, {Op: 2}, {Op: 9}, {Op: 33, A: 2}, {Op: 11}, {Op: 30, A: 3, Flags: 1}, {Op: 30, A: 4, B: 1, Flags: 3}, {Op: 12, A: 1}, {Op: 9}, {Op: 11, A: 1}, {Op: 25}, {Op: 28, A: 35}, {Op: 5}, {Op: 33, A: 5}, {Op: 1, A: 1}, {Op: 1, A: 2}, {Op: 30, A: 3}, {Op: 13}, {Op: 11}, {Op: 30, A: 6, Flags: 1}, {Op: 30, A: 3}, {Op: 13}, {Op: 1, A: 3}, {Op: 30, A: 3}, {Op: 13}, {Op: 30, A: 7, B: 2, Flags: 2}, {Op: 26, A: 36}, {Op: 2}, {Op: 9}, {Op: 11, A: 1}, {Op: 1, A: 4}, {Op: 30, A: 8, B: 1, Flags: 3}, {Op: 10}, {Op: 27, A: 44}, {Op: 9}, {Op: 1}, {Op: 12, A: 2}, {Op: 9}, {Op: 11, A: 2}, {Op: 33}, {Op: 11, A: 1}, {Op: 1, A: 5}, {Op: 30, A: 8, B: 1, Flags: 3}, {Op: 30, A: 9, B: 1, Flags: 3}, {Op: 6, A: 2}, {Op: 53}}, + Insns: []bytecode.Instr{{Op: 11}, {Op: 33}, {Op: 30, A: 1, B: 1, Flags: 3}, {Op: 28, A: 9}, {Op: 1}, {Op: 11}, {Op: 6, A: 2}, {Op: 53}, {Op: 26, A: 10}, {Op: 2}, {Op: 9}, {Op: 33, A: 2}, {Op: 11}, {Op: 30, A: 3, Flags: 1}, {Op: 30, A: 4, B: 1, Flags: 3}, {Op: 12, A: 1}, {Op: 9}, {Op: 11, A: 1}, {Op: 25}, {Op: 28, A: 30}, {Op: 5}, {Op: 33, A: 5}, {Op: 1, A: 1}, {Op: 11}, {Op: 30, A: 6, Flags: 1}, {Op: 93}, {Op: 1, A: 2}, {Op: 92, A: 3}, {Op: 30, A: 7, B: 2, Flags: 2}, {Op: 26, A: 31}, {Op: 2}, {Op: 9}, {Op: 11, A: 1}, {Op: 1, A: 3}, {Op: 30, A: 8, B: 1, Flags: 3}, {Op: 10}, {Op: 27, A: 39}, {Op: 9}, {Op: 1}, {Op: 12, A: 2}, {Op: 9}, {Op: 11, A: 2}, {Op: 33}, {Op: 11, A: 1}, {Op: 1, A: 4}, {Op: 30, A: 8, B: 1, Flags: 3}, {Op: 30, A: 9, B: 1, Flags: 3}, {Op: 6, A: 2}, {Op: 53}}, Consts: []object.Value{ object.FString("=", ""), - object.NewString(""), object.FString("Illformed requirement [", ""), object.FString("]", ""), object.IntValue(1), @@ -5066,7 +5040,7 @@ func embeddedPrelude() *bytecode.ISeq { BlockSlot: -1, NumLocals: 3, Locals: []string{"obj", "m", "op"}, - Lines: []bytecode.LineEntry{{PC: 0, Line: 1642}, {PC: 11, Line: 1643}, {PC: 17, Line: 1644}, {PC: 37, Line: 1645}, {PC: 46, Line: 1646}}, + Lines: []bytecode.LineEntry{{PC: 0, Line: 1642}, {PC: 11, Line: 1643}, {PC: 17, Line: 1644}, {PC: 32, Line: 1645}, {PC: 41, Line: 1646}}, FirstLine: 1641, Super: "", }, @@ -5122,13 +5096,11 @@ func embeddedPrelude() *bytecode.ISeq { Children: []*bytecode.ISeq{ &bytecode.ISeq{ Name: "block in to_s", - Insns: []bytecode.Instr{{Op: 1}, {Op: 1, A: 1}, {Op: 30}, {Op: 13}, {Op: 11}, {Op: 30}, {Op: 13}, {Op: 1, A: 2}, {Op: 30}, {Op: 13}, {Op: 11, A: 1}, {Op: 30}, {Op: 13}, {Op: 1, A: 1}, {Op: 30}, {Op: 13}, {Op: 53}}, + Insns: []bytecode.Instr{{Op: 1}, {Op: 11}, {Op: 93}, {Op: 1, A: 1}, {Op: 11, A: 1}, {Op: 93}, {Op: 1}, {Op: 92, A: 5}, {Op: 53}}, Consts: []object.Value{ - object.NewString(""), object.FString("", ""), object.FString(" ", ""), }, - Names: []string{"to_s"}, Params: []string{"op", "req"}, NumRequired: 2, SplatIndex: -1, @@ -5413,11 +5385,10 @@ func embeddedPrelude() *bytecode.ISeq { }, &bytecode.ISeq{ Name: "method_missing", - Insns: []bytecode.Instr{{Op: 11}, {Op: 30, Flags: 1}, {Op: 12, A: 2}, {Op: 9}, {Op: 11, A: 2}, {Op: 1}, {Op: 30, A: 1, B: 1, Flags: 3}, {Op: 28, A: 46}, {Op: 11, A: 1}, {Op: 30, A: 2, Flags: 1}, {Op: 1, A: 1}, {Op: 22}, {Op: 25}, {Op: 28, A: 29}, {Op: 5}, {Op: 33, A: 3}, {Op: 1, A: 2}, {Op: 1, A: 3}, {Op: 30}, {Op: 13}, {Op: 11, A: 1}, {Op: 30, A: 2, Flags: 1}, {Op: 30}, {Op: 13}, {Op: 1, A: 4}, {Op: 30}, {Op: 13}, {Op: 30, A: 4, B: 2, Flags: 2}, {Op: 26, A: 30}, {Op: 2}, {Op: 9}, {Op: 31, A: 5}, {Op: 11, A: 2}, {Op: 1, A: 5}, {Op: 1, A: 6}, {Op: 8}, {Op: 30, A: 6, B: 1, Flags: 3}, {Op: 30, A: 7, Flags: 1}, {Op: 11, A: 1}, {Op: 1, A: 5}, {Op: 30, A: 6, B: 1, Flags: 3}, {Op: 12, A: 3}, {Op: 30, A: 8, B: 2, Flags: 3}, {Op: 9}, {Op: 11, A: 3}, {Op: 26, A: 54}, {Op: 11, A: 1}, {Op: 30, A: 9, Flags: 1}, {Op: 28, A: 53}, {Op: 31, A: 5}, {Op: 11}, {Op: 30, A: 6, B: 1, Flags: 3}, {Op: 26, A: 54}, {Op: 47, B: 1}, {Op: 53}}, + Insns: []bytecode.Instr{{Op: 11}, {Op: 30, Flags: 1}, {Op: 12, A: 2}, {Op: 9}, {Op: 11, A: 2}, {Op: 1}, {Op: 30, A: 1, B: 1, Flags: 3}, {Op: 28, A: 41}, {Op: 11, A: 1}, {Op: 30, A: 2, Flags: 1}, {Op: 1, A: 1}, {Op: 22}, {Op: 25}, {Op: 28, A: 24}, {Op: 5}, {Op: 33, A: 3}, {Op: 1, A: 2}, {Op: 11, A: 1}, {Op: 30, A: 2, Flags: 1}, {Op: 93}, {Op: 1, A: 3}, {Op: 92, A: 3}, {Op: 30, A: 4, B: 2, Flags: 2}, {Op: 26, A: 25}, {Op: 2}, {Op: 9}, {Op: 31, A: 5}, {Op: 11, A: 2}, {Op: 1, A: 4}, {Op: 1, A: 5}, {Op: 8}, {Op: 30, A: 6, B: 1, Flags: 3}, {Op: 30, A: 7, Flags: 1}, {Op: 11, A: 1}, {Op: 1, A: 4}, {Op: 30, A: 6, B: 1, Flags: 3}, {Op: 12, A: 3}, {Op: 30, A: 8, B: 2, Flags: 3}, {Op: 9}, {Op: 11, A: 3}, {Op: 26, A: 49}, {Op: 11, A: 1}, {Op: 30, A: 9, Flags: 1}, {Op: 28, A: 48}, {Op: 31, A: 5}, {Op: 11}, {Op: 30, A: 6, B: 1, Flags: 3}, {Op: 26, A: 49}, {Op: 47, B: 1}, {Op: 53}}, Consts: []object.Value{ object.FString("=", ""), object.IntValue(1), - object.NewString(""), object.FString("wrong number of arguments (given ", ""), object.FString(", expected 1)", ""), object.IntValue(0), @@ -5432,7 +5403,7 @@ func embeddedPrelude() *bytecode.ISeq { BlockSlot: -1, NumLocals: 4, Locals: []string{"name", "args", "n", ""}, - Lines: []bytecode.LineEntry{{PC: 0, Line: 1743}, {PC: 4, Line: 1744}, {PC: 8, Line: 1745}, {PC: 31, Line: 1746}, {PC: 49, Line: 1748}, {PC: 53, Line: 1750}}, + Lines: []bytecode.LineEntry{{PC: 0, Line: 1743}, {PC: 4, Line: 1744}, {PC: 8, Line: 1745}, {PC: 26, Line: 1746}, {PC: 44, Line: 1748}, {PC: 48, Line: 1750}}, FirstLine: 1742, Super: "", }, @@ -5767,32 +5738,30 @@ func embeddedPrelude() *bytecode.ISeq { }, &bytecode.ISeq{ Name: "__getobj__", - Insns: []bytecode.Instr{{Op: 5}, {Op: 33}, {Op: 1}, {Op: 1, A: 1}, {Op: 30, A: 1}, {Op: 13}, {Op: 5}, {Op: 30, A: 2}, {Op: 30, A: 1}, {Op: 13}, {Op: 1, A: 2}, {Op: 30, A: 1}, {Op: 13}, {Op: 30, A: 3, B: 2, Flags: 2}, {Op: 53}}, + Insns: []bytecode.Instr{{Op: 5}, {Op: 33}, {Op: 1}, {Op: 5}, {Op: 30, A: 1}, {Op: 93}, {Op: 1, A: 1}, {Op: 92, A: 3}, {Op: 30, A: 2, B: 2, Flags: 2}, {Op: 53}}, Consts: []object.Value{ - object.NewString(""), object.FString("", ""), object.FString("#__getobj__ is not implemented", ""), }, - Names: []string{"NotImplementedError", "to_s", "class", "raise"}, + Names: []string{"NotImplementedError", "class", "raise"}, NumRequired: 0, SplatIndex: -1, PostCount: 0, KwRestSlot: -1, BlockSlot: -1, NumLocals: 0, - Lines: []bytecode.LineEntry{{PC: 0, Line: 1859}, {PC: 6, Line: 109}}, + Lines: []bytecode.LineEntry{{PC: 0, Line: 1859}, {PC: 3, Line: 109}}, FirstLine: 1858, Super: "", }, &bytecode.ISeq{ Name: "__setobj__", - Insns: []bytecode.Instr{{Op: 5}, {Op: 33}, {Op: 1}, {Op: 1, A: 1}, {Op: 30, A: 1}, {Op: 13}, {Op: 5}, {Op: 30, A: 2}, {Op: 30, A: 1}, {Op: 13}, {Op: 1, A: 2}, {Op: 30, A: 1}, {Op: 13}, {Op: 30, A: 3, B: 2, Flags: 2}, {Op: 53}}, + Insns: []bytecode.Instr{{Op: 5}, {Op: 33}, {Op: 1}, {Op: 5}, {Op: 30, A: 1}, {Op: 93}, {Op: 1, A: 1}, {Op: 92, A: 3}, {Op: 30, A: 2, B: 2, Flags: 2}, {Op: 53}}, Consts: []object.Value{ - object.NewString(""), object.FString("", ""), object.FString("#__setobj__ is not implemented", ""), }, - Names: []string{"NotImplementedError", "to_s", "class", "raise"}, + Names: []string{"NotImplementedError", "class", "raise"}, Params: []string{"_obj"}, NumRequired: 1, SplatIndex: -1, @@ -5801,7 +5770,7 @@ func embeddedPrelude() *bytecode.ISeq { BlockSlot: -1, NumLocals: 1, Locals: []string{"_obj"}, - Lines: []bytecode.LineEntry{{PC: 0, Line: 1863}, {PC: 6, Line: 109}}, + Lines: []bytecode.LineEntry{{PC: 0, Line: 1863}, {PC: 3, Line: 109}}, FirstLine: 1862, Super: "", }, @@ -6031,13 +6000,12 @@ func embeddedPrelude() *bytecode.ISeq { }, &bytecode.ISeq{ Name: "inspect", - Insns: []bytecode.Instr{{Op: 1}, {Op: 1, A: 1}, {Op: 30}, {Op: 13}, {Op: 31, A: 1}, {Op: 30}, {Op: 13}, {Op: 1, A: 2}, {Op: 30}, {Op: 13}, {Op: 53}}, + Insns: []bytecode.Instr{{Op: 1}, {Op: 31}, {Op: 93}, {Op: 1, A: 1}, {Op: 92, A: 3}, {Op: 53}}, Consts: []object.Value{ - object.NewString(""), object.FString("#", ""), }, - Names: []string{"to_s", "@path"}, + Names: []string{"@path"}, NumRequired: 0, SplatIndex: -1, PostCount: 0, @@ -6833,18 +6801,15 @@ func embeddedPrelude() *bytecode.ISeq { }, &bytecode.ISeq{ Name: "render_attribute", - Insns: []bytecode.Instr{{Op: 11, A: 1}, {Op: 30, Flags: 1}, {Op: 10}, {Op: 27, A: 8}, {Op: 9}, {Op: 11, A: 1}, {Op: 4}, {Op: 22}, {Op: 28, A: 12}, {Op: 1}, {Op: 53}, {Op: 26, A: 13}, {Op: 2}, {Op: 9}, {Op: 11, A: 1}, {Op: 3}, {Op: 22}, {Op: 28, A: 30}, {Op: 1, A: 1}, {Op: 1, A: 2}, {Op: 30, A: 1}, {Op: 13}, {Op: 11}, {Op: 30, A: 1}, {Op: 13}, {Op: 1, A: 3}, {Op: 30, A: 1}, {Op: 13}, {Op: 53}, {Op: 26, A: 31}, {Op: 2}, {Op: 9}, {Op: 11, A: 1}, {Op: 33, A: 2}, {Op: 30, A: 3, B: 1, Flags: 3}, {Op: 28, A: 55}, {Op: 1, A: 4}, {Op: 1, A: 2}, {Op: 30, A: 1}, {Op: 13}, {Op: 11}, {Op: 30, A: 1}, {Op: 13}, {Op: 1, A: 5}, {Op: 30, A: 1}, {Op: 13}, {Op: 11, A: 1}, {Op: 30, A: 4, Flags: 1}, {Op: 30, A: 1}, {Op: 13}, {Op: 1, A: 6}, {Op: 30, A: 1}, {Op: 13}, {Op: 53}, {Op: 26, A: 56}, {Op: 2}, {Op: 9}, {Op: 1, A: 7}, {Op: 1, A: 2}, {Op: 30, A: 1}, {Op: 13}, {Op: 11}, {Op: 30, A: 1}, {Op: 13}, {Op: 1, A: 5}, {Op: 30, A: 1}, {Op: 13}, {Op: 5}, {Op: 11, A: 1}, {Op: 30, A: 5, B: 1, Flags: 2}, {Op: 30, A: 1}, {Op: 13}, {Op: 1, A: 6}, {Op: 30, A: 1}, {Op: 13}, {Op: 53}}, + Insns: []bytecode.Instr{{Op: 11, A: 1}, {Op: 30, Flags: 1}, {Op: 10}, {Op: 27, A: 8}, {Op: 9}, {Op: 11, A: 1}, {Op: 4}, {Op: 22}, {Op: 28, A: 12}, {Op: 1}, {Op: 53}, {Op: 26, A: 13}, {Op: 2}, {Op: 9}, {Op: 11, A: 1}, {Op: 3}, {Op: 22}, {Op: 28, A: 25}, {Op: 1, A: 1}, {Op: 11}, {Op: 93}, {Op: 1, A: 2}, {Op: 92, A: 3}, {Op: 53}, {Op: 26, A: 26}, {Op: 2}, {Op: 9}, {Op: 11, A: 1}, {Op: 33, A: 1}, {Op: 30, A: 2, B: 1, Flags: 3}, {Op: 28, A: 42}, {Op: 1, A: 1}, {Op: 11}, {Op: 93}, {Op: 1, A: 3}, {Op: 11, A: 1}, {Op: 30, A: 3, Flags: 1}, {Op: 93}, {Op: 1, A: 4}, {Op: 92, A: 5}, {Op: 53}, {Op: 26, A: 43}, {Op: 2}, {Op: 9}, {Op: 1, A: 1}, {Op: 11}, {Op: 93}, {Op: 1, A: 3}, {Op: 5}, {Op: 11, A: 1}, {Op: 30, A: 4, B: 1, Flags: 2}, {Op: 93}, {Op: 1, A: 4}, {Op: 92, A: 5}, {Op: 53}}, Consts: []object.Value{ object.FString("", ""), - object.NewString(""), object.FString(" ", ""), object.FString("=\"\"", ""), - object.NewString(""), object.FString("=\"", ""), object.FString("\"", ""), - object.NewString(""), }, - Names: []string{"nil?", "to_s", "Safe", "is_a?", "value", "escape_html"}, + Names: []string{"nil?", "Safe", "is_a?", "value", "escape_html"}, Params: []string{"name", "value"}, NumRequired: 2, SplatIndex: -1, @@ -6853,7 +6818,7 @@ func embeddedPrelude() *bytecode.ISeq { BlockSlot: -1, NumLocals: 2, Locals: []string{"name", "value"}, - Lines: []bytecode.LineEntry{{PC: 0, Line: 2231}, {PC: 14, Line: 2232}, {PC: 22, Line: 1}, {PC: 32, Line: 2233}, {PC: 40, Line: 1}, {PC: 57, Line: 2235}, {PC: 61, Line: 1}}, + Lines: []bytecode.LineEntry{{PC: 0, Line: 2231}, {PC: 14, Line: 2232}, {PC: 19, Line: 1}, {PC: 27, Line: 2233}, {PC: 32, Line: 1}, {PC: 44, Line: 2235}, {PC: 45, Line: 1}}, FirstLine: 2230, Super: "", }, @@ -7196,13 +7161,12 @@ func embeddedPrelude() *bytecode.ISeq { Children: []*bytecode.ISeq{ &bytecode.ISeq{ Name: "block (2 levels) in render", - Insns: []bytecode.Instr{{Op: 11, A: 1, B: 2}, {Op: 1}, {Op: 1, A: 1}, {Op: 30}, {Op: 13}, {Op: 11}, {Op: 30}, {Op: 13}, {Op: 1, A: 2}, {Op: 30}, {Op: 13}, {Op: 11, A: 1}, {Op: 12, A: 2}, {Op: 30, A: 1, B: 2, Flags: 3}, {Op: 9}, {Op: 11, A: 2}, {Op: 53}}, + Insns: []bytecode.Instr{{Op: 11, A: 1, B: 2}, {Op: 1}, {Op: 11}, {Op: 93}, {Op: 1, A: 1}, {Op: 92, A: 3}, {Op: 11, A: 1}, {Op: 12, A: 2}, {Op: 30, B: 2, Flags: 3}, {Op: 9}, {Op: 11, A: 2}, {Op: 53}}, Consts: []object.Value{ - object.NewString(""), object.FString("data-", ""), object.FString("", ""), }, - Names: []string{"to_s", "[]="}, + Names: []string{"[]="}, Params: []string{"dk", "dv"}, NumRequired: 2, SplatIndex: -1, @@ -7220,16 +7184,14 @@ func embeddedPrelude() *bytecode.ISeq { }, &bytecode.ISeq{ Name: "block in render", - Insns: []bytecode.Instr{{Op: 11, A: 1, B: 1}, {Op: 11}, {Op: 30, B: 1, Flags: 3}, {Op: 12, A: 1}, {Op: 9}, {Op: 33, A: 1}, {Op: 11}, {Op: 30, A: 2, B: 1, Flags: 3}, {Op: 28, A: 32}, {Op: 11, A: 1}, {Op: 10}, {Op: 28, A: 16}, {Op: 9}, {Op: 11, A: 1}, {Op: 4}, {Op: 23}, {Op: 28, A: 30}, {Op: 11, A: 2, B: 1}, {Op: 1}, {Op: 1, A: 1}, {Op: 30, A: 3}, {Op: 13}, {Op: 11}, {Op: 30, A: 3}, {Op: 13}, {Op: 1, A: 2}, {Op: 30, A: 3}, {Op: 13}, {Op: 30, A: 4, B: 1}, {Op: 26, A: 31}, {Op: 2}, {Op: 26, A: 62}, {Op: 11, A: 1}, {Op: 30, A: 5, Flags: 1}, {Op: 28, A: 38}, {Op: 2}, {Op: 53}, {Op: 26, A: 39}, {Op: 2}, {Op: 9}, {Op: 11, A: 2, B: 1}, {Op: 1, A: 3}, {Op: 1, A: 1}, {Op: 30, A: 3}, {Op: 13}, {Op: 11}, {Op: 30, A: 3}, {Op: 13}, {Op: 1, A: 4}, {Op: 30, A: 3}, {Op: 13}, {Op: 33, A: 6}, {Op: 34, A: 7}, {Op: 11, A: 1}, {Op: 30, A: 3, Flags: 1}, {Op: 30, A: 8, B: 1, Flags: 3}, {Op: 30, A: 3}, {Op: 13}, {Op: 1, A: 5}, {Op: 30, A: 3}, {Op: 13}, {Op: 30, A: 4, B: 1}, {Op: 53}}, + Insns: []bytecode.Instr{{Op: 11, A: 1, B: 1}, {Op: 11}, {Op: 30, B: 1, Flags: 3}, {Op: 12, A: 1}, {Op: 9}, {Op: 33, A: 1}, {Op: 11}, {Op: 30, A: 2, B: 1, Flags: 3}, {Op: 28, A: 27}, {Op: 11, A: 1}, {Op: 10}, {Op: 28, A: 16}, {Op: 9}, {Op: 11, A: 1}, {Op: 4}, {Op: 23}, {Op: 28, A: 25}, {Op: 11, A: 2, B: 1}, {Op: 1}, {Op: 11}, {Op: 93}, {Op: 1, A: 1}, {Op: 92, A: 3}, {Op: 30, A: 3, B: 1}, {Op: 26, A: 26}, {Op: 2}, {Op: 26, A: 49}, {Op: 11, A: 1}, {Op: 30, A: 4, Flags: 1}, {Op: 28, A: 33}, {Op: 2}, {Op: 53}, {Op: 26, A: 34}, {Op: 2}, {Op: 9}, {Op: 11, A: 2, B: 1}, {Op: 1}, {Op: 11}, {Op: 93}, {Op: 1, A: 2}, {Op: 33, A: 5}, {Op: 34, A: 6}, {Op: 11, A: 1}, {Op: 30, A: 7, Flags: 1}, {Op: 30, A: 8, B: 1, Flags: 3}, {Op: 93}, {Op: 1, A: 3}, {Op: 92, A: 5}, {Op: 30, A: 3, B: 1}, {Op: 53}}, Consts: []object.Value{ - object.NewString(""), object.FString(" ", ""), object.FString("", ""), - object.NewString(""), object.FString("=\"", ""), object.FString("\"", ""), }, - Names: []string{"[]", "BOOL", "include?", "to_s", "<<", "nil?", "Haml", "Util", "escape_html"}, + Names: []string{"[]", "BOOL", "include?", "<<", "nil?", "Haml", "Util", "to_s", "escape_html"}, Params: []string{"k"}, NumRequired: 1, SplatIndex: -1, @@ -7238,7 +7200,7 @@ func embeddedPrelude() *bytecode.ISeq { BlockSlot: -1, NumLocals: 2, Locals: []string{"k", "v"}, - Lines: []bytecode.LineEntry{{PC: 0, Line: 2349}, {PC: 5, Line: 2350}, {PC: 9, Line: 2351}, {PC: 32, Line: 2353}, {PC: 40, Line: 2355}, {PC: 45, Line: 1}}, + Lines: []bytecode.LineEntry{{PC: 0, Line: 2349}, {PC: 5, Line: 2350}, {PC: 9, Line: 2351}, {PC: 27, Line: 2353}, {PC: 35, Line: 2355}, {PC: 37, Line: 1}}, FirstLine: 2348, Super: "", }, @@ -7710,15 +7672,14 @@ func embeddedPrelude() *bytecode.ISeq { Children: []*bytecode.ISeq{ &bytecode.ISeq{ Name: "block in initialize", - Insns: []bytecode.Instr{{Op: 1}, {Op: 1, A: 1}, {Op: 30}, {Op: 13}, {Op: 11, A: 4, B: 1}, {Op: 30}, {Op: 13}, {Op: 1, A: 1}, {Op: 30}, {Op: 13}, {Op: 33, A: 1}, {Op: 30, A: 2, Flags: 1}, {Op: 30}, {Op: 13}, {Op: 1, A: 2}, {Op: 30}, {Op: 13}, {Op: 33, A: 3}, {Op: 30, A: 4, Flags: 1}, {Op: 30}, {Op: 13}, {Op: 1, A: 2}, {Op: 30}, {Op: 13}, {Op: 5}, {Op: 1, A: 3}, {Op: 30, A: 5, B: 1, Flags: 2}, {Op: 1, A: 4}, {Op: 30, B: 1, Flags: 3}, {Op: 30}, {Op: 13}, {Op: 1, A: 1}, {Op: 30}, {Op: 13}, {Op: 11, A: 5, B: 1}, {Op: 30}, {Op: 13}, {Op: 1, A: 1}, {Op: 30}, {Op: 13}, {Op: 12}, {Op: 9}, {Op: 33, A: 6}, {Op: 11, A: 6, B: 1}, {Op: 11}, {Op: 30, A: 7, B: 2, Flags: 3}, {Op: 12, A: 1}, {Op: 9}, {Op: 33, A: 6}, {Op: 11, A: 1}, {Op: 30, A: 8, B: 1, Flags: 3}, {Op: 25}, {Op: 28, A: 59}, {Op: 11, A: 1}, {Op: 32, A: 9}, {Op: 9}, {Op: 2}, {Op: 55}, {Op: 26, A: 60}, {Op: 2}, {Op: 53}}, + Insns: []bytecode.Instr{{Op: 1}, {Op: 11, A: 4, B: 1}, {Op: 93}, {Op: 1}, {Op: 33}, {Op: 30, A: 1, Flags: 1}, {Op: 93}, {Op: 1, A: 1}, {Op: 33, A: 2}, {Op: 30, A: 3, Flags: 1}, {Op: 93}, {Op: 1, A: 1}, {Op: 5}, {Op: 1, A: 2}, {Op: 30, A: 4, B: 1, Flags: 2}, {Op: 1, A: 3}, {Op: 30, A: 5, B: 1, Flags: 3}, {Op: 93}, {Op: 1}, {Op: 11, A: 5, B: 1}, {Op: 93}, {Op: 1}, {Op: 92, A: 11}, {Op: 12}, {Op: 9}, {Op: 33, A: 6}, {Op: 11, A: 6, B: 1}, {Op: 11}, {Op: 30, A: 7, B: 2, Flags: 3}, {Op: 12, A: 1}, {Op: 9}, {Op: 33, A: 6}, {Op: 11, A: 1}, {Op: 30, A: 8, B: 1, Flags: 3}, {Op: 25}, {Op: 28, A: 42}, {Op: 11, A: 1}, {Op: 32, A: 9}, {Op: 9}, {Op: 2}, {Op: 55}, {Op: 26, A: 43}, {Op: 2}, {Op: 53}}, Consts: []object.Value{ - object.NewString(""), object.FString("", ""), object.FString("-", ""), object.IntValue(4294967296), object.IntValue(36), }, - Names: []string{"to_s", "Process", "pid", "Tempfile", "counter", "rand", "File", "join", "exist?", "@path"}, + Names: []string{"Process", "pid", "Tempfile", "counter", "rand", "to_s", "File", "join", "exist?", "@path"}, NumRequired: 0, SplatIndex: -1, PostCount: 0, @@ -7726,7 +7687,7 @@ func embeddedPrelude() *bytecode.ISeq { BlockSlot: -1, NumLocals: 2, Locals: []string{"name", "candidate"}, - Lines: []bytecode.LineEntry{{PC: 0, Line: 2497}, {PC: 42, Line: 2498}, {PC: 48, Line: 2499}, {PC: 53, Line: 2500}, {PC: 56, Line: 2501}}, + Lines: []bytecode.LineEntry{{PC: 0, Line: 2497}, {PC: 25, Line: 2498}, {PC: 31, Line: 2499}, {PC: 36, Line: 2500}, {PC: 39, Line: 2501}}, FirstLine: 2496, Super: "", }, @@ -7957,16 +7918,14 @@ func embeddedPrelude() *bytecode.ISeq { Children: []*bytecode.ISeq{ &bytecode.ISeq{ Name: "[]", - Insns: []bytecode.Instr{{Op: 11}, {Op: 33}, {Op: 30, A: 1, B: 1, Flags: 3}, {Op: 25}, {Op: 28, A: 20}, {Op: 5}, {Op: 33, A: 2}, {Op: 1}, {Op: 1, A: 1}, {Op: 30, A: 3}, {Op: 13}, {Op: 11}, {Op: 30, A: 4, Flags: 1}, {Op: 30, A: 3}, {Op: 13}, {Op: 1, A: 2}, {Op: 30, A: 3}, {Op: 13}, {Op: 30, A: 5, B: 2, Flags: 2}, {Op: 26, A: 21}, {Op: 2}, {Op: 9}, {Op: 31, A: 6}, {Op: 11}, {Op: 30, A: 7, B: 1, Flags: 3}, {Op: 25}, {Op: 28, A: 41}, {Op: 5}, {Op: 33, A: 8}, {Op: 1, A: 3}, {Op: 1, A: 4}, {Op: 30, A: 3}, {Op: 13}, {Op: 11}, {Op: 30, A: 3}, {Op: 13}, {Op: 1, A: 5}, {Op: 30, A: 3}, {Op: 13}, {Op: 30, A: 5, B: 2, Flags: 2}, {Op: 26, A: 42}, {Op: 2}, {Op: 9}, {Op: 31, A: 6}, {Op: 11}, {Op: 30, A: 9, B: 1, Flags: 3}, {Op: 53}}, + Insns: []bytecode.Instr{{Op: 11}, {Op: 33}, {Op: 30, A: 1, B: 1, Flags: 3}, {Op: 25}, {Op: 28, A: 15}, {Op: 5}, {Op: 33, A: 2}, {Op: 1}, {Op: 11}, {Op: 30, A: 3, Flags: 1}, {Op: 93}, {Op: 1, A: 1}, {Op: 92, A: 3}, {Op: 30, A: 4, B: 2, Flags: 2}, {Op: 26, A: 16}, {Op: 2}, {Op: 9}, {Op: 31, A: 5}, {Op: 11}, {Op: 30, A: 6, B: 1, Flags: 3}, {Op: 25}, {Op: 28, A: 31}, {Op: 5}, {Op: 33, A: 7}, {Op: 1, A: 2}, {Op: 11}, {Op: 93}, {Op: 1, A: 3}, {Op: 92, A: 3}, {Op: 30, A: 4, B: 2, Flags: 2}, {Op: 26, A: 32}, {Op: 2}, {Op: 9}, {Op: 31, A: 5}, {Op: 11}, {Op: 30, A: 8, B: 1, Flags: 3}, {Op: 53}}, Consts: []object.Value{ - object.NewString(""), object.FString("wrong argument type ", ""), object.FString(" (expected Symbol)", ""), - object.NewString(""), object.FString("unknown category: ", ""), object.FString("", ""), }, - Names: []string{"Symbol", "is_a?", "TypeError", "to_s", "class", "raise", "@warning_categories", "key?", "ArgumentError", "[]"}, + Names: []string{"Symbol", "is_a?", "TypeError", "class", "raise", "@warning_categories", "key?", "ArgumentError", "[]"}, Params: []string{"category"}, NumRequired: 1, SplatIndex: -1, @@ -7975,22 +7934,20 @@ func embeddedPrelude() *bytecode.ISeq { BlockSlot: -1, NumLocals: 1, Locals: []string{"category"}, - Lines: []bytecode.LineEntry{{PC: 0, Line: 2604}, {PC: 5, Line: 2605}, {PC: 22, Line: 2607}, {PC: 27, Line: 2608}, {PC: 43, Line: 2610}}, + Lines: []bytecode.LineEntry{{PC: 0, Line: 2604}, {PC: 5, Line: 2605}, {PC: 17, Line: 2607}, {PC: 22, Line: 2608}, {PC: 33, Line: 2610}}, FirstLine: 2603, Super: "", }, &bytecode.ISeq{ Name: "[]=", - Insns: []bytecode.Instr{{Op: 11}, {Op: 33}, {Op: 30, A: 1, B: 1, Flags: 3}, {Op: 25}, {Op: 28, A: 20}, {Op: 5}, {Op: 33, A: 2}, {Op: 1}, {Op: 1, A: 1}, {Op: 30, A: 3}, {Op: 13}, {Op: 11}, {Op: 30, A: 4, Flags: 1}, {Op: 30, A: 3}, {Op: 13}, {Op: 1, A: 2}, {Op: 30, A: 3}, {Op: 13}, {Op: 30, A: 5, B: 2, Flags: 2}, {Op: 26, A: 21}, {Op: 2}, {Op: 9}, {Op: 31, A: 6}, {Op: 11}, {Op: 30, A: 7, B: 1, Flags: 3}, {Op: 25}, {Op: 28, A: 41}, {Op: 5}, {Op: 33, A: 8}, {Op: 1, A: 3}, {Op: 1, A: 4}, {Op: 30, A: 3}, {Op: 13}, {Op: 11}, {Op: 30, A: 3}, {Op: 13}, {Op: 1, A: 5}, {Op: 30, A: 3}, {Op: 13}, {Op: 30, A: 5, B: 2, Flags: 2}, {Op: 26, A: 42}, {Op: 2}, {Op: 9}, {Op: 31, A: 6}, {Op: 11}, {Op: 11, A: 1}, {Op: 12, A: 2}, {Op: 30, A: 9, B: 2, Flags: 3}, {Op: 9}, {Op: 11, A: 2}, {Op: 53}}, + Insns: []bytecode.Instr{{Op: 11}, {Op: 33}, {Op: 30, A: 1, B: 1, Flags: 3}, {Op: 25}, {Op: 28, A: 15}, {Op: 5}, {Op: 33, A: 2}, {Op: 1}, {Op: 11}, {Op: 30, A: 3, Flags: 1}, {Op: 93}, {Op: 1, A: 1}, {Op: 92, A: 3}, {Op: 30, A: 4, B: 2, Flags: 2}, {Op: 26, A: 16}, {Op: 2}, {Op: 9}, {Op: 31, A: 5}, {Op: 11}, {Op: 30, A: 6, B: 1, Flags: 3}, {Op: 25}, {Op: 28, A: 31}, {Op: 5}, {Op: 33, A: 7}, {Op: 1, A: 2}, {Op: 11}, {Op: 93}, {Op: 1, A: 3}, {Op: 92, A: 3}, {Op: 30, A: 4, B: 2, Flags: 2}, {Op: 26, A: 32}, {Op: 2}, {Op: 9}, {Op: 31, A: 5}, {Op: 11}, {Op: 11, A: 1}, {Op: 12, A: 2}, {Op: 30, A: 8, B: 2, Flags: 3}, {Op: 9}, {Op: 11, A: 2}, {Op: 53}}, Consts: []object.Value{ - object.NewString(""), object.FString("wrong argument type ", ""), object.FString(" (expected Symbol)", ""), - object.NewString(""), object.FString("unknown category: ", ""), object.FString("", ""), }, - Names: []string{"Symbol", "is_a?", "TypeError", "to_s", "class", "raise", "@warning_categories", "key?", "ArgumentError", "[]="}, + Names: []string{"Symbol", "is_a?", "TypeError", "class", "raise", "@warning_categories", "key?", "ArgumentError", "[]="}, Params: []string{"category", "flag"}, NumRequired: 2, SplatIndex: -1, @@ -7999,7 +7956,7 @@ func embeddedPrelude() *bytecode.ISeq { BlockSlot: -1, NumLocals: 3, Locals: []string{"category", "flag", ""}, - Lines: []bytecode.LineEntry{{PC: 0, Line: 2616}, {PC: 5, Line: 2617}, {PC: 22, Line: 2619}, {PC: 27, Line: 2620}, {PC: 43, Line: 2622}}, + Lines: []bytecode.LineEntry{{PC: 0, Line: 2616}, {PC: 5, Line: 2617}, {PC: 17, Line: 2619}, {PC: 22, Line: 2620}, {PC: 33, Line: 2622}}, FirstLine: 2615, Super: "", }, diff --git a/internal/vm/vm.go b/internal/vm/vm.go index ffadbeb3..b2f220a6 100644 --- a/internal/vm/vm.go +++ b/internal/vm/vm.go @@ -609,51 +609,55 @@ type VM struct { // paperTrail holds the model-versioning state (require "paper_trail"): the // shared version store, per-class has_paper_trail config, request whodunnit // and the injectable clock. Created once by registerPaperTrail. - paperTrail *paperTrailState - ajBases map[*RClass]*activejob.Base // ActiveJob: library job class built per `class … < ActiveJob::Base` subclass (class-dispatch seam) - ajJobOf map[*RObject]*activejob.Job // ActiveJob: Ruby job instance -> backing library *Job - ajInstOf map[*activejob.Job]*RObject // ActiveJob: library *Job -> Ruby job instance - ajTestAdapters map[*RClass]*activejob.TestAdapter // ActiveJob: per-class :test adapter - ajStack []*RObject // ActiveJob: instance stack the inline #perform seam reads - ajLastResult object.Value // ActiveJob: last #perform return value (perform_now's result) - ajArgs *activejob.Arguments // ActiveJob: module-level ActiveJob::Arguments serializer (GlobalID seam wired in registerActiveJob) - cActionMailerBase *RClass // ActionMailer::Base, the superclass every mailer subclasses (require "action_mailer"), backed by go-ruby-actionmailer - cActionMailerDelivery *RClass // ActionMailer::MessageDelivery, the lazy proxy a mailer action returns - cActionMailerAttachments *RClass // the internal `attachments` proxy class (Mail::AttachmentsList analogue) - amDefs map[*RClass]*amDef // ActionMailer: per-mailer-class default/delivery/hook declarations, merged down the chain at delivery time - amDeliveries *object.Array // ActionMailer: the shared ActionMailer::Base.deliveries Array the :test delivery method appends to - sftpClasses *sftpClassSet // Net::SFTP: the wrapper classes stamped on returned Attributes / Name / File values (require "net/sftp") - amMailerOf map[*RObject]*actionmailer.Mailer // ActionMailer: mailer instance -> library *Mailer of its running action (the mail/attachments/headers seam) - amRenderer object.Value // ActionMailer: the Ruby renderer the RenderBody seam sends #render to (Action View wiring / test stub) - amEnqueuer object.Value // ActionMailer: the Ruby enqueuer the deliver_later seam sends #enqueue to (Active Job wiring / test stub) - asConfig *activestorage.Config // ActiveStorage process config (require "active_storage"); nil until first use, then a deterministic in-process config (MemStore + DiskService temp dir) - cACChannelBase *RClass // ActionCable::Channel::Base, the superclass a subscription's channel subclass extends, backed by go-ruby-actioncable - acServer object.Value // memoized ActionCable.server singleton (an ActionCable::Server over an in-process async adapter) - railtieSeams map[any]*railtieSeam // per-railtie/engine/app deferred initializer blocks, keyed by the library ctx object; run inline by the RunInitializer seam during Application#initialize! - railsApp *RailsAppVal // the Ruby Application registered via Rails.application= (the rails meta-gem's top-level app); nil before boot - deviseConfig *DeviseConfig // the shared Devise.config the DatabaseAuthenticatable Warden strategy authenticates against - cHanamiRouter *RClass // Hanami::Router (require "hanami/router"), backed by go-ruby-hanami; wraps a *hanami.Router - cHanamiAction *RClass // Hanami::Action (require "hanami/action"), the action-lifecycle superclass a user subclasses - cHanamiRequest *RClass // Hanami::Action::Request, the request handed to a Hanami action's #handle - cHanamiResponse *RClass // Hanami::Action::Response, the mutable response a Hanami action's #handle writes into - cHanamiFlash *RClass // Hanami::Action::Flash, the two-generation flash store on the request/response - hanamiActionDefs map[*RClass]*hanamiActionDef // per-Hanami::Action-subclass before/after/handle_exception/accept/config declarations - cACRouteSet *RClass // ActionDispatch::Routing::RouteSet (require "action_dispatch"), backed by go-ruby-actionpack/routing - cACMapper *RClass // ActionDispatch::Routing::Mapper, the self the routes DSL (draw) runs against - cACRequest *RClass // ActionDispatch::Request, over a Rack env - cACResponse *RClass // ActionDispatch::Response, the mutable Rack response - cACParameters *RClass // ActionController::Parameters (strong parameters) - cACControllerBase *RClass // ActionController::Base (require "action_controller"), the controller superclass a user subclasses - acControllerDefs map[*RClass]*acControllerDef // per-ActionController::Base-subclass before/after/around/rescue_from/view-context declarations - cMinitestSpec *RClass // Minitest::Spec, the spec-DSL subclass of Minitest::Test - minitestRunnables []*RClass // Minitest::Test subclasses registered via the inherited hook, in definition order (the autorun run set) - minitestCurInstance object.Value // the test instance currently running (backs bare must_*/wont_* and _) - minitestAutorunDone bool // guards the require "minitest/autorun" at_exit hook against a double run - factoryBotReg *factorybot.Registry // the per-VM factory_bot registry (require "factory_bot"): FactoryBot.define/build/create map onto it; the Build/Persist/Block seams are wired to this VM's object model - facterFacter *facter.Facter // the per-VM Facter adapter (require "facter"), from go-ruby-facter over go-facter: Facter.value/[]/fact/add/to_hash read it; custom facts (Facter.add … setcode) never leak across interpreters - fastGettext *fastgettext.Instance // the per-VM FastGettext instance (require "fast_gettext"), from go-ruby-fast-gettext: text domains, current locale/text_domain and the translation cache never leak across interpreters - cOpenSSLDigest *RClass - cArray, cHash, cRange *RClass + paperTrail *paperTrailState + ajBases map[*RClass]*activejob.Base // ActiveJob: library job class built per `class … < ActiveJob::Base` subclass (class-dispatch seam) + ajJobOf map[*RObject]*activejob.Job // ActiveJob: Ruby job instance -> backing library *Job + ajInstOf map[*activejob.Job]*RObject // ActiveJob: library *Job -> Ruby job instance + ajTestAdapters map[*RClass]*activejob.TestAdapter // ActiveJob: per-class :test adapter + ajStack []*RObject // ActiveJob: instance stack the inline #perform seam reads + ajLastResult object.Value // ActiveJob: last #perform return value (perform_now's result) + ajArgs *activejob.Arguments // ActiveJob: module-level ActiveJob::Arguments serializer (GlobalID seam wired in registerActiveJob) + cActionMailerBase *RClass // ActionMailer::Base, the superclass every mailer subclasses (require "action_mailer"), backed by go-ruby-actionmailer + cActionMailerDelivery *RClass // ActionMailer::MessageDelivery, the lazy proxy a mailer action returns + cActionMailerAttachments *RClass // the internal `attachments` proxy class (Mail::AttachmentsList analogue) + amDefs map[*RClass]*amDef // ActionMailer: per-mailer-class default/delivery/hook declarations, merged down the chain at delivery time + amDeliveries *object.Array // ActionMailer: the shared ActionMailer::Base.deliveries Array the :test delivery method appends to + sftpClasses *sftpClassSet // Net::SFTP: the wrapper classes stamped on returned Attributes / Name / File values (require "net/sftp") + amMailerOf map[*RObject]*actionmailer.Mailer // ActionMailer: mailer instance -> library *Mailer of its running action (the mail/attachments/headers seam) + amRenderer object.Value // ActionMailer: the Ruby renderer the RenderBody seam sends #render to (Action View wiring / test stub) + amEnqueuer object.Value // ActionMailer: the Ruby enqueuer the deliver_later seam sends #enqueue to (Active Job wiring / test stub) + asConfig *activestorage.Config // ActiveStorage process config (require "active_storage"); nil until first use, then a deterministic in-process config (MemStore + DiskService temp dir) + cACChannelBase *RClass // ActionCable::Channel::Base, the superclass a subscription's channel subclass extends, backed by go-ruby-actioncable + acServer object.Value // memoized ActionCable.server singleton (an ActionCable::Server over an in-process async adapter) + railtieSeams map[any]*railtieSeam // per-railtie/engine/app deferred initializer blocks, keyed by the library ctx object; run inline by the RunInitializer seam during Application#initialize! + railsApp *RailsAppVal // the Ruby Application registered via Rails.application= (the rails meta-gem's top-level app); nil before boot + deviseConfig *DeviseConfig // the shared Devise.config the DatabaseAuthenticatable Warden strategy authenticates against + cHanamiRouter *RClass // Hanami::Router (require "hanami/router"), backed by go-ruby-hanami; wraps a *hanami.Router + cHanamiAction *RClass // Hanami::Action (require "hanami/action"), the action-lifecycle superclass a user subclasses + cHanamiRequest *RClass // Hanami::Action::Request, the request handed to a Hanami action's #handle + cHanamiResponse *RClass // Hanami::Action::Response, the mutable response a Hanami action's #handle writes into + cHanamiFlash *RClass // Hanami::Action::Flash, the two-generation flash store on the request/response + hanamiActionDefs map[*RClass]*hanamiActionDef // per-Hanami::Action-subclass before/after/handle_exception/accept/config declarations + cACRouteSet *RClass // ActionDispatch::Routing::RouteSet (require "action_dispatch"), backed by go-ruby-actionpack/routing + cACMapper *RClass // ActionDispatch::Routing::Mapper, the self the routes DSL (draw) runs against + cACRequest *RClass // ActionDispatch::Request, over a Rack env + cACResponse *RClass // ActionDispatch::Response, the mutable Rack response + cACParameters *RClass // ActionController::Parameters (strong parameters) + cACControllerBase *RClass // ActionController::Base (require "action_controller"), the controller superclass a user subclasses + acControllerDefs map[*RClass]*acControllerDef // per-ActionController::Base-subclass before/after/around/rescue_from/view-context declarations + cMinitestSpec *RClass // Minitest::Spec, the spec-DSL subclass of Minitest::Test + minitestRunnables []*RClass // Minitest::Test subclasses registered via the inherited hook, in definition order (the autorun run set) + minitestCurInstance object.Value // the test instance currently running (backs bare must_*/wont_* and _) + minitestAutorunDone bool // guards the require "minitest/autorun" at_exit hook against a double run + factoryBotReg *factorybot.Registry // the per-VM factory_bot registry (require "factory_bot"): FactoryBot.define/build/create map onto it; the Build/Persist/Block seams are wired to this VM's object model + facterFacter *facter.Facter // the per-VM Facter adapter (require "facter"), from go-ruby-facter over go-facter: Facter.value/[]/fact/add/to_hash read it; custom facts (Facter.add … setcode) never leak across interpreters + fastGettext *fastgettext.Instance // the per-VM FastGettext instance (require "fast_gettext"), from go-ruby-fast-gettext: text domains, current locale/text_domain and the translation cache never leak across interpreters + cOpenSSLDigest *RClass + cArray, cHash, cRange *RClass + // basicOps records what each specialised operator resolved to at the end of + // bootstrap, so the operator opcodes can tell a built-in from a + // redefinition. See basic_op_redefinition.go. + basicOps *basicOpSnapshot cProc *RClass cMethod *RClass cEnumerator *RClass @@ -1295,6 +1299,18 @@ func NewWithStderr(out, errOut io.Writer) *VM { vm.includeStringIOEnumerable() // StringIO mixes in Enumerable; after the prelude so the module exists vm.finishARGF() // ARGF.class mixes in Enumerable and ARGF.argv binds to ARGV; after the prelude, and after ARGV exists vm.installHashKeyHook() + // Record what each specialised operator resolves to while nothing has touched + // it, so the operator opcodes can tell a built-in from a redefinition. + // + // It has to be HERE and not at the end of bootstrap: bootstrap registers the + // natives, and the prelude — which supplies Comparable#< and so the ordering + // operators of every Comparable class — runs after it. Snapshotting too early + // recorded nil for Integer#<, so the guard saw Comparable#< as "a + // redefinition", dispatched it, and its body's own `< 0` came straight back: + // `1 < 2` recursed until the Go stack was exhausted, with nothing redefined + // at all. Measured, on 18 of the 88 (class, operator) pairs. The snapshot and + // the guard must therefore ask the same question at the same time. + vm.snapshotBasicOperators() // The prelude and built-ins are loaded; arm the level-2 AOT top level so the // next Run (the user program) dispatches to the compiled aotMain, if one was // linked in. @@ -3126,6 +3142,46 @@ func (vm *VM) exec(iseq *bytecode.ISeq, self object.Value, args []object.Value, elems = append(elems, a2.Elems...) elems = append(elems, b2.Elems...) push(object.NewArrayFromSlice(elems)) + case bytecode.OpObjToString: + // MRI's objtostring, with its anytostring fallback: a String is + // pushed back untouched, anything else goes through #to_s, and a + // #to_s that does not answer with a String falls back to + // rb_any_to_s of the ORIGINAL value. + // + // It is a CALL SITE, so it resolves refinements the way OpSend + // does -- objtostring carries its own call data in MRI, and + // ruby/spec pins it: "Module#refine for methods accessed + // indirectly | is honored by string interpolation". Dispatching + // through vm.send alone lost that, because vm.send has no lexical + // scope to resolve against: + // + // module M; refine Integer do def to_s; "foo"; end end; end + // using M + // "#{1}" ruby 4.0.5 "foo" through vm.send "1" + // + // #puts does NOT honour it, on either side -- rb_obj_as_string + // goes through rb_funcall, which has no scope either (measured: + // ruby prints 1). So the refinement belongs HERE and not in + // displayStr. + // The T_STRING arm lives in objToStringWith, not here: duplicating it + // left the helper's own copy unreached, which the coverage gate + // reported. A refinement is resolved first because only this loop + // knows the lexical scope, and it is skipped for a String there. + otsV := pop() + var otsRef *Method + if vm.anyRefinements && stringTypeOf(otsV) == nil { + otsRef = refinedFor(otsV, "to_s") + } + push(vm.objToStringWith(otsV, otsRef)) + case bytecode.OpConcatStrings: + // MRI's concatstrings: join A already-pushed parts into one fresh, + // unfrozen String. Deliberately NOT String#+ — see the compiler's + // StrInterp case and basic_op_redefinition.go. + n := in.A + parts := stack[len(stack)-n:] + joined := vm.concatStringParts(parts) + stack = stack[:len(stack)-n] + push(joined) case bytecode.OpSendArray: argsArr := pop().(*object.Array) recv := pop() diff --git a/internal/vm/wave28_sleep_scheduler_test.go b/internal/vm/wave28_sleep_scheduler_test.go index e68fc6f8..ef26b29f 100644 --- a/internal/vm/wave28_sleep_scheduler_test.go +++ b/internal/vm/wave28_sleep_scheduler_test.go @@ -33,6 +33,21 @@ class S end Fiber.set_scheduler(S.new) ` + // Three rows below ask for a real sleep's RESULT, and test it the way + // TestWave28SleepReturnsWholeSeconds does -- `[0, 1].include?(…)` -- rather + // than pinning 0. rb_f_sleep brackets the wait with time(0), so its result is + // the number of whole-second boundaries CROSSED, not the duration rounded: + // a 1ms sleep answers 1 whenever it straddles one, in MRI exactly as here. + // Reproduced on ruby 4.0.5 and on both rbgo builds by waiting until 2ms + // before a boundary: + // + // sleep(0.005) straddling a boundary => 1 + // sleep(0.005) mid-second => 0 + // + // Pinning 0 made these rows fail whenever the process was descheduled across + // a boundary, which is a near-certainty on a loaded CI runner: that is what + // took down both POSIX lanes of one run, with nothing in the change touching + // sleep. cases := []struct{ src, want string }{ // A non-blocking fiber routes sleep to the scheduler; with no argument the // hook is called with no argument (kernel_sleepv forwards argc verbatim), @@ -53,7 +68,7 @@ Fiber.set_scheduler(S.new) // ...and it never coerces them, so a String reaches the hook unrefused. {sched + `f = Fiber.new(blocking: false) { sleep("2") }; f.resume; p Fiber.scheduler.events`, "[[:kernel_sleep, [\"2\"]]]\n"}, // Uninstalling the scheduler puts the ordinary path back. - {sched + `Fiber.set_scheduler(nil); p Fiber.new(blocking: false) { sleep(0.001) }.resume`, "0\n"}, + {sched + `Fiber.set_scheduler(nil); p [0, 1].include?(Fiber.new(blocking: false) { sleep(0.001) }.resume)`, "true\n"}, // rb_time_interval, numeric cases. {`p sleep(0)`, "0\n"}, @@ -96,7 +111,7 @@ Fiber.set_scheduler(S.new) {`p sleep(Rational(1, 999)).class`, "Integer\n"}, {`o = Object.new; def o.divmod(*); [0, 0.001]; end; p sleep(o).class`, "Integer\n"}, // NUM2TIMET truncates a Float quotient rather than refusing it. - {`o = Object.new; def o.divmod(*); [0.5, 0]; end; p sleep(o)`, "0\n"}, + {`o = Object.new; def o.divmod(*); [0.5, 0]; end; p [0, 1].include?(sleep(o))`, "true\n"}, // arg_range_check applies to the whole-seconds half only. {`begin; sleep(Rational(-1, 2)); rescue => e; p [e.class, e.message]; end`, "[ArgumentError, \"time interval must not be negative\"]\n"}, // The quotient goes through NUM2TIMET, which is rb_num2long: it names nil @@ -112,7 +127,7 @@ Fiber.set_scheduler(S.new) {`begin; sleep(:s); rescue => e; p [e.class, e.message]; end`, "[TypeError, \"can't convert Symbol into time interval\"]\n"}, // A #divmod reached only through method_missing still converts, as // rb_check_funcall's respond_to_missing? probe allows. - {`o = Object.new; def o.respond_to_missing?(n, p = false); n == :divmod; end; def o.method_missing(n, *a); n == :divmod ? [0, 0.001] : super; end; p sleep(o)`, "0\n"}, + {`o = Object.new; def o.respond_to_missing?(n, p = false); n == :divmod; end; def o.method_missing(n, *a); n == :divmod ? [0, 0.001] : super; end; p [0, 1].include?(sleep(o))`, "true\n"}, // rb_check_arity(argc, 0, 1) guards the duration branch of rb_f_sleep. {`begin; sleep(1, 2); rescue => e; p [e.class, e.message]; end`, "[ArgumentError, \"wrong number of arguments (given 2, expected 0..1)\"]\n"}, diff --git a/internal/vm/wave57_guard_internals_test.go b/internal/vm/wave57_guard_internals_test.go new file mode 100644 index 00000000..8beeb1ab --- /dev/null +++ b/internal/vm/wave57_guard_internals_test.go @@ -0,0 +1,220 @@ +// Copyright (c) the go-embedded-ruby/ruby authors +// +// SPDX-License-Identifier: BSD-3-Clause + +package vm + +import ( + "io" + "testing" + + "github.com/go-embedded-ruby/ruby/internal/bytecode" + "github.com/go-embedded-ruby/ruby/internal/object" +) + +// The guards below are reachable but not reached by any Ruby program the suite +// runs, so the coverage gate named them. They are exercised directly rather +// than left recorded: each one decides something, and a decision nothing +// checks is a decision that can be quietly inverted. + +// TestBasicOpNameRefusesANonOperator: basicOpName answers "" for an opcode that +// is not one of the eleven, and the guard's callers return "no override" on +// that. The arm matters because the function used to be built on arithOpName, +// whose own default returns "%" -- which silently gave every comparison opcode +// the wrong name. +func TestBasicOpNameRefusesANonOperator(t *testing.T) { + for _, op := range []bytecode.Op{bytecode.OpPop, bytecode.OpDup, bytecode.OpJump, bytecode.OpLast} { + if got := basicOpName(op); got != "" { + t.Errorf("basicOpName(%v) = %q, want \"\" for a non-operator opcode", op, got) + } + } + // and every opcode it DOES name, as the control + for _, op := range basicOps { + if basicOpName(op) == "" { + t.Errorf("basicOpName(%v) = \"\", but it is in basicOps", op) + } + } +} + +// TestSnapshotAncestorStopsAtAnUnwatchedChain: a class with no basic-operator +// ancestor has no record to compare against, and the guard must then keep the +// inline path rather than treat "nothing recorded" as "redefined". +func TestSnapshotAncestorStopsAtAnUnwatchedChain(t *testing.T) { + vm := New(io.Discard) + if vm.basicOps == nil || !vm.basicOps.taken { + t.Fatal("the snapshot is not taken; the rest of this test means nothing") + } + // BasicObject is above every watched class, so walking up from it finds none. + if got := vm.snapshotAncestor(vm.cBasicObject); got != nil { + t.Errorf("snapshotAncestor(BasicObject) = %v, want nil", got.name) + } + // The control: a watched class finds itself, and a subclass finds its base. + if got := vm.snapshotAncestor(vm.cString); got != vm.cString { + t.Errorf("snapshotAncestor(String) = %v, want String", got) + } + sub := newClass("StringSub", vm.cString) + if got := vm.snapshotAncestor(sub); got != vm.cString { + t.Errorf("snapshotAncestor(StringSub) = %v, want String", got) + } +} + +// TestBasicOpWasDefinedAcrossTheChain: the send path's operator fallback asks +// this before resurrecting a removed operator. It must answer for a watched +// class, for a subclass of one, and for neither. +func TestBasicOpWasDefinedAcrossTheChain(t *testing.T) { + vm := New(io.Discard) + one := object.IntValue(1) + if !vm.basicOpWasDefined(bytecode.OpAdd, one) { + t.Error("Integer#+ was defined at snapshot time; basicOpWasDefined said no") + } + if vm.basicOpWasDefined(bytecode.OpAdd, object.SymVal("s")) { + t.Error("Symbol has no #+ in the snapshot; basicOpWasDefined said it had") + } + // A subclass instance answers through its ancestor's record. + sub := newClass("IntSub", vm.cInteger) + inst := &RObject{class: sub, ivars: map[string]object.Value{}} + if !vm.basicOpWasDefined(bytecode.OpAdd, inst) { + t.Error("a subclass of Integer must inherit the record for #+") + } + // And a class in no watched chain answers false rather than guessing. + bare := &RObject{class: newClass("Bare", vm.cBasicObject), ivars: map[string]object.Value{}} + if vm.basicOpWasDefined(bytecode.OpAdd, bare) { + t.Error("a class outside every watched chain must answer false") + } + // Before the snapshot nothing can have been redefined, so it answers false. + fresh := &VM{} + if fresh.basicOpWasDefined(bytecode.OpAdd, one) { + t.Error("with no snapshot taken, basicOpWasDefined must answer false") + } +} + +// TestInstallOperatorMethodKeepsAnExistingOne: the installer must never replace +// an operator a registration site already defined -- a site that chose a body +// wins over this fallback. Both arms are exercised: the install, and the no-op. +func TestInstallOperatorMethodKeepsAnExistingOne(t *testing.T) { + vm := New(io.Discard) + marker := object.SymVal("original") + + // Arm 1: the name is free, so it installs and the body is the opcode's. + fresh := newClass("Fresh", vm.cObject) + installOperatorMethod(fresh, bytecode.OpAdd) + m := fresh.methods["+"] + if m == nil { + t.Fatal("installOperatorMethod did not install #+ on a class without one") + } + if !m.argc.declared { + t.Error("the installed method declares no arity; MRI's is 1") + } + if got := vm.invoke(m, object.IntValue(2), []object.Value{object.IntValue(3)}, nil); got != object.IntValue(5) { + t.Errorf("installed #+ computed %v, want 5", got) + } + + // Arm 2: the name is taken, so it is left alone. + taken := newClass("Taken", vm.cObject) + taken.defineArgc("+", 1, func(_ *VM, _ object.Value, _ []object.Value, _ *Proc) object.Value { + return marker + }) + before := taken.methods["+"] + installOperatorMethod(taken, bytecode.OpAdd) + if taken.methods["+"] != before { + t.Error("installOperatorMethod replaced a method a registration site had defined") + } + if got := vm.invoke(taken.methods["+"], object.NilV, []object.Value{object.IntValue(1)}, nil); got != marker { + t.Errorf("the original body no longer runs: got %v", got) + } +} + +// TestBasicOpClassesHoldsNoNil: the nil filter is in basicOpClasses, so the +// snapshot loop needs no per-class guard. If a class ever is nil -- a +// build-tagged registration skipped -- it is dropped here and stays unwatched, +// which keeps its operators on the inline path rather than reading "no record" +// as "redefined". +func TestBasicOpClassesHoldsNoNil(t *testing.T) { + vm := New(io.Discard) + got := vm.basicOpClasses() + if len(got) == 0 { + t.Fatal("no basic operator classes at all") + } + for i, c := range got { + if c == nil { + t.Errorf("basicOpClasses()[%d] is nil; the filter did not run", i) + } + } + // And a VM with a nil field drops it rather than returning it. + partial := &VM{cInteger: vm.cInteger} + for i, c := range partial.basicOpClasses() { + if c == nil { + t.Errorf("basicOpClasses()[%d] is nil on a partially built VM", i) + } + } + if n := len(partial.basicOpClasses()); n != 1 { + t.Errorf("a VM with one class set returned %d, want 1", n) + } +} + +// TestResolveBasicOpOverrideIgnoresANonOperator is the slow path's own guard: an +// opcode with no operator name has no override to find. +func TestResolveBasicOpOverrideIgnoresANonOperator(t *testing.T) { + vm := New(io.Discard) + if m := vm.resolveBasicOpOverride(bytecode.OpDup, object.IntValue(1), vm.cInteger); m != nil { + t.Errorf("resolveBasicOpOverride(OpDup) = %v, want nil", m.name) + } +} + +// TestOperatorOpcodeMapsEveryName: operatorOpcode is the name -> opcode map the +// send path's fallback and defined? consult. Its `-` case stopped being reached +// once Integer#- had a real record, so the gate reported it; the mapping is +// what the function IS, so it is asserted rather than left to a caller. +func TestOperatorOpcodeMapsEveryName(t *testing.T) { + for name, want := range map[string]bytecode.Op{ + "+": bytecode.OpAdd, + "-": bytecode.OpSub, + "*": bytecode.OpMul, + "/": bytecode.OpDiv, + "%": bytecode.OpMod, + } { + got, ok := operatorOpcode(name) + if !ok { + t.Errorf("operatorOpcode(%q) reported no opcode", name) + continue + } + if got != want { + t.Errorf("operatorOpcode(%q) = %v, want %v", name, got, want) + } + // and the names round-trip through basicOpName + if back := basicOpName(got); back != name { + t.Errorf("basicOpName(operatorOpcode(%q)) = %q", name, back) + } + } + for _, name := range []string{"==", "<", "**", "[]", "to_s", ""} { + if _, ok := operatorOpcode(name); ok { + t.Errorf("operatorOpcode(%q) claimed an opcode; only the five arithmetic names have one", name) + } + } +} + +// TestNoRecordBeforeAndNoneNowKeepsTheInlinePath: a watched class that never +// had this operator -- Hash has no #+ in ruby either -- must keep the inline +// path, so the opcode produces MRI's own error rather than this guard reading +// "nothing resolves" as "it was removed". +func TestNoRecordBeforeAndNoneNowKeepsTheInlinePath(t *testing.T) { + vm := New(io.Discard) + h := object.NewHash() + if m := vm.resolveBasicOpOverride(bytecode.OpAdd, h, vm.cHash); m != nil { + t.Errorf("resolveBasicOpOverride for Hash#+ = %v, want nil (no record then, none now)", m.name) + } + // The control: Integer#+ HAS a record, so the same call answers nil for a + // different reason -- it resolves to the record itself, not to nothing. + if m := vm.resolveBasicOpOverride(bytecode.OpAdd, object.IntValue(1), vm.cInteger); m != nil { + t.Errorf("resolveBasicOpOverride for an untouched Integer#+ = %v, want nil", m.name) + } + // And the error the inline path gives for Hash#+ is the one ruby gives. + const src = `begin + p({a: 1} + {b: 2}) +rescue => e + puts "#{e.class}" +end` + if got := eval(t, src); got != "TypeError\n" { + t.Errorf("Hash#+ raised %q; the inline path must still produce its own error", got) + } +} diff --git a/internal/vm/wave57_op_guard_cost_test.go b/internal/vm/wave57_op_guard_cost_test.go new file mode 100644 index 00000000..9c15a158 --- /dev/null +++ b/internal/vm/wave57_op_guard_cost_test.go @@ -0,0 +1,80 @@ +// Copyright (c) the go-embedded-ruby/ruby authors +// +// SPDX-License-Identifier: BSD-3-Clause + +package vm + +import ( + "strconv" + "testing" +) + +// TestOperatorGuardCostsNoResolutionPerIteration: the redefinition guard sits on +// every operator evaluation, so the question that decides whether it is +// affordable is how often it RESOLVES a method -- once per loop, or once per +// iteration. +// +// It is asserted as a count rather than timed on purpose. A wall-clock or even +// a CPU-time comparison on this machine is worthless while other work is +// running (measured at load 127, the same binary varied by 3x run to run), and +// the property being claimed is not "fast" but "does no work per iteration", +// which a counter settles exactly and a clock cannot. +// +// The bound is deliberately loose: what matters is that it does not scale with +// the iteration count. 30000 iterations over three operators would cost 90000 +// resolutions without the cache. +func TestOperatorGuardCostsNoResolutionPerIteration(t *testing.T) { + const iters = 30000 + src := `t = 0 +i = 0 +while i < ` + strconv.Itoa(iters) + ` + t = t + i * 2 + i = i + 1 +end +p t +` + before := basicOpSlowPath.Load() + eval(t, src) + misses := basicOpSlowPath.Load() - before + + // Four operators appear (+, *, <, and the loop's own +), each on Integer, so + // a correct cache warms a handful of entries and then stops resolving. + const bound = 64 + if misses > bound { + t.Errorf("guard resolved a method %d times over %d iterations; want <= %d\n"+ + "a count that scales with the iteration count means the per-operator cache is not holding", + misses, iters, bound) + } + t.Logf("%d resolutions for %d iterations (%d operator evaluations)", misses, iters, iters*4) +} + +// TestOperatorGuardResolvesAgainAfterARedefinition is the other half: the cache +// must not be so sticky that it misses a change. A redefinition bumps +// globalMethodSerial, which invalidates every entry, so the count MUST move. +// +// It is an A/B over two programs of the same shape differing in one line, +// because each eval builds a FRESH VM: comparing a "warm" run against a later +// one would compare two cold caches and prove nothing. That is how the first +// version of this test passed its subject and failed its own premise. +func TestOperatorGuardResolvesAgainAfterARedefinition(t *testing.T) { + loop := `i = 0 +while i < 500 + i = i + 1 +end +p i +` + count := func(src string) uint64 { + before := basicOpSlowPath.Load() + eval(t, src) + return basicOpSlowPath.Load() - before + } + plain := count(loop + loop) + redefined := count(loop + "class Integer; def +(o); self.-(-o); end; end\n" + loop) + + if redefined <= plain { + t.Errorf("same program with a redefinition in the middle cost %d resolutions, without it %d;\n"+ + "a redefinition must invalidate the cache and force at least one more", + redefined, plain) + } + t.Logf("resolutions: %d without a redefinition, %d with one", plain, redefined) +} diff --git a/internal/vm/wave57_op_redefinition_test.go b/internal/vm/wave57_op_redefinition_test.go new file mode 100644 index 00000000..07a72141 --- /dev/null +++ b/internal/vm/wave57_op_redefinition_test.go @@ -0,0 +1,228 @@ +// Copyright (c) the go-embedded-ruby/ruby authors +// +// SPDX-License-Identifier: BSD-3-Clause + +package vm + +import "testing" + +// TestOperatorOpcodeHonoursARedefinition: the operator opcodes specialise for +// the built-in value types, and MRI guards every such specialisation with +// BASIC_OP_UNREDEFINED_P. Without that guard the same call answered twice over: +// +// class Integer; def +(o); :HIJACKED; end; end +// 1 + 2 ruby 4.0.5 :HIJACKED before 3 +// 1.send(:+, 2) ruby 4.0.5 :HIJACKED before :HIJACKED +// +// Each row below is a separate eval, because a redefinition is global: measured +// over all 88 (class, operator) pairs the opcodes reach, 60 diverged, and #send +// answered correctly in every one of them. +func TestOperatorOpcodeHonoursARedefinition(t *testing.T) { + for _, tc := range []struct{ name, src, want string }{ + {"Integer+", `class Integer; def +(o); :H; end; end +p 1 + 2 +p 1.send(:+, 2)`, ":H\n:H\n"}, + {"Integer<", `class Integer; def <(o); :H; end; end +p 1 < 2`, ":H\n"}, + {"Integer==", `class Integer; def ==(o); :H; end; end +p 1 == 2`, ":H\n"}, + {"Float*", `class Float; def *(o); :H; end; end +p 2.0 * 3.0`, ":H\n"}, + {"Float>=", `class Float; def >=(o); :H; end; end +p 1.0 >= 2.0`, ":H\n"}, + {"String+", `class String; def +(o); :H; end; end +p "a" + "b"`, ":H\n"}, + {"String%", `class String; def %(o); :H; end; end +p "a" % "b"`, ":H\n"}, + {"Array+", `class Array; def +(o); :H; end; end +p [1] + [2]`, ":H\n"}, + {"Hash==", `class Hash; def ==(o); :H; end; end +p({a: 1} == {b: 2})`, ":H\n"}, + {"Symbol<=", `class Symbol; def <=(o); :H; end; end +p :a <= :b`, ":H\n"}, + {"NilClass==", `class NilClass; def ==(o); :H; end; end +p nil == nil`, ":H\n"}, + } { + t.Run(tc.name, func(t *testing.T) { + if got := eval(t, tc.src); got != tc.want { + t.Errorf("got %q want %q", got, tc.want) + } + }) + } +} + +// TestOperatorOpcodeSeesSubclassesAndPrepends: the guard resolves on the +// ORIGINAL receiver through the full dispatch chain, not on the unwrapped +// built-in value, so a subclass's own operator and a prepended module's are +// both found. The middle row is the control -- a subclass that does NOT +// override keeps the inline path and the built-in answer. +func TestOperatorOpcodeSeesSubclassesAndPrepends(t *testing.T) { + src := `class S < String; def +(o); :SUB; end; end +class T < String; end +module M; def +(o); :MOD; end; end +class U < String; prepend M; end +p(S.new("a") + "b") +p(T.new("a") + "b") +p(U.new("a") + "b") +o = Object.new +def o.+(x); :SING; end +p(o + 1) +` + const want = ":SUB\n\"ab\"\n:MOD\n:SING\n" + if got := eval(t, src); got != want { + t.Errorf("got %q\nwant %q", got, want) + } +} + +// TestOperatorCacheSeesAMidRunRedefinition: the guard caches its answer per +// operator, keyed on the receiver class and globalMethodSerial, so the usual +// case costs no method resolution. The cache must therefore be invalidated by a +// redefinition that happens AFTER the operator has already been used -- which +// the census could not test, since every case there redefines before using. +// +// Two operators and two classes, because an entry is per OPERATOR: a cache +// keyed per class alone would answer String#+ out of Integer's entry. +func TestOperatorCacheSeesAMidRunRedefinition(t *testing.T) { + src := `p 1 + 2 +p "a" + "b" +p 1 * 2 +class Integer; def +(o); :I; end; end +class String; def +(o); :S; end; end +p 1 + 2 +p "a" + "b" +p 1 * 2 +` + const want = "3\n\"ab\"\n2\n:I\n:S\n2\n" + if got := eval(t, src); got != want { + t.Errorf("got %q\nwant %q", got, want) + } +} + +// TestRemovingABuiltinOperatorRaises: installing the operator records makes +// `remove_method :+` SUCCEED where it used to raise NameError, so the removal +// has to take effect -- otherwise the new records would have replaced a +// NameError with a silent no-op. MRI raises from both the opcode and #send. +func TestRemovingABuiltinOperatorRaises(t *testing.T) { + src := `class Integer; remove_method :+; end +begin; p 1 + 2; rescue => e; puts "#{e.class}: #{e.message}"; end +begin; p 1.send(:+, 2); rescue => e; puts "#{e.class}: #{e.message}"; end +` + const want = "NoMethodError: undefined method '+' for an instance of Integer\n" + + "NoMethodError: undefined method '+' for an instance of Integer\n" + if got := eval(t, src); got != want { + t.Errorf("got %q\nwant %q", got, want) + } +} + +// TestOperatorMethodRecordsMatchMRI: #755 reported Integer and Float missing a +// record for + - * /. Measured across nine classes it was fourteen, not eight. +// The rows are ruby 4.0.5's own answers, including the ones that must STAY +// absent -- String has no #- or #/ and Array no #/ or #%, so a blanket +// installation would have been wrong in the other direction. +func TestOperatorMethodRecordsMatchMRI(t *testing.T) { + src := `ops = %w[+ - * / % < > <= >= == !=] +[Integer, Float, String, Array, Hash, Symbol, NilClass, TrueClass, FalseClass].each do |c| + have = ops.select { |o| begin; c.instance_method(o.to_sym); true; rescue; false; end } + puts "#{c}: #{have.join(' ')}" +end +p Integer.instance_method(:+).arity +p Integer.instance_method(:+).owner +p 1.method(:+).call(2) +` + const want = `Integer: + - * / % < > <= >= == != +Float: + - * / % < > <= >= == != +String: + * % < > <= >= == != +Array: + - * == != +Hash: < > <= >= == != +Symbol: < > <= >= == != +NilClass: == != +TrueClass: == != +FalseClass: == != +1 +Integer +3 +` + if got := eval(t, src); got != want { + t.Errorf("got\n%s\nwant\n%s", got, want) + } +} + +// TestInterpolationDoesNotDispatchStringOperators: interpolation compiled to +// `"" + part.to_s + …`, so it depended on String#+ and sent #to_s to every +// segment including the literal ones. MRI emits concatstrings and sends its +// tostring only to the embedded expressions. Both rows were observable: the +// #to_s one on main already (ruby "A1B", rbgo TypeError), the #+ one as soon as +// the operator opcodes started honouring a redefinition. +func TestInterpolationDoesNotDispatchStringOperators(t *testing.T) { + for _, tc := range []struct{ name, src, want string }{ + {"to_s", `class String; def to_s; :TOS; end; end +p "A#{1}B"`, "\"A1B\"\n"}, + {"plus", `class String; def +(o); :P; end; end +p "A#{1}B"`, "\"A1B\"\n"}, + {"both", `class String; def to_s; :T; end; def +(o); :P; end; end +p "A#{1}B#{2}C"`, "\"A1B2C\"\n"}, + // A value that IS a String -- subclass included -- is interpolated + // UNTOUCHED, which a plain `send :to_s` gets wrong in both directions at + // once: it would answer "SUB" here while #to_s itself must still answer + // "SUB" when asked directly. + {"string subclass", `class S < String; def to_s; "SUB"; end; end +p "#{S.new("x")}" +p S.new("x").to_s`, "\"x\"\n\"SUB\"\n"}, + {"plain string subclass", `class T < String; end +p "#{T.new("y")}"`, "\"y\"\n"}, + {"frozen", `p "a#{1}".frozen?`, "false\n"}, + {"encoding", `p "é#{1}".encoding.to_s`, "\"UTF-8\"\n"}, + } { + t.Run(tc.name, func(t *testing.T) { + if got := eval(t, tc.src); got != tc.want { + t.Errorf("got %q want %q", got, tc.want) + } + }) + } +} + +// TestInterpolationHonoursARefinement: the interpolation opcode is a CALL SITE, +// so it resolves refinements the way a send does -- MRI's objtostring carries +// its own call data, and ruby/spec pins it as "Module#refine for methods +// accessed indirectly | is honored by string interpolation". +// +// Dispatching through a plain Go-level send lost it, because that has no +// lexical scope to resolve against. The conformance ratchet caught it as the +// one REGRESSED file in a run that gained 22 examples across 15 others -- which +// a scalar total of +22 would have hidden. +// +// #puts does NOT honour it, on either side: rb_obj_as_string goes through +// rb_funcall, which has no scope either. That row is the control, and it is +// ruby's answer as much as rbgo's. +func TestInterpolationHonoursARefinement(t *testing.T) { + src := `module M + refine Integer do + def to_s; "foo"; end + end +end +using M +puts "#{1}" +puts 1.to_s +puts 1 +` + const want = "foo\nfoo\n1\n" + if got := eval(t, src); got != want { + t.Errorf("got %q want %q", got, want) + } +} + +// TestInterpolationFallsBackToAnyToS is MRI's anytostring: when #to_s does not +// return a String, the fallback renders the VALUE with rb_any_to_s rather than +// raising. The address is masked, since it is an address. +func TestInterpolationFallsBackToAnyToS(t *testing.T) { + src := `class Foo; def to_s; :notastring; end; end +class Bar; def to_s; 42; end; end +puts "x#{Foo.new}y".sub(/0x[0-9a-f]+/, "0xA") +puts "p#{Bar.new}q".sub(/0x[0-9a-f]+/, "0xA") +puts "e#{nil}f" +` + const want = "x#y\np#q\nef\n" + if got := eval(t, src); got != want { + t.Errorf("got %q\nwant %q", got, want) + } +} diff --git a/scripts/coverage/BELOW100.macos-latest b/scripts/coverage/BELOW100.macos-latest index 1c2358df..885a48f3 100644 --- a/scripts/coverage/BELOW100.macos-latest +++ b/scripts/coverage/BELOW100.macos-latest @@ -17,6 +17,14 @@ # is not done: that cost is not observable at program level -- five runs of # 200000 puts gave overlapping distributions -- and a second serial-validated # cache for an unobservable effect is complexity bought with nothing. +# The three defined.go rows are an operator SPECIAL CASE that fourteen new +# operator records made unreachable for Integer/Float/String/Array -- defined? +# now finds the method the ordinary way. It is NOT removed, because it still +# serves the receivers binaryOp computes on WITHOUT a record: Bignum, +# Rational, Complex, Time. Settling that needs a defined? census across every +# such receiver against ruby, which is its own measured wave (issue filed), so +# the arms stay and are recorded rather than deleted on a guess. +# # Recorded 2026-09-28 from run 36424534618, main @ 1a79138b, CI's own measurement. internal/object/object.go repr 90.9% internal/vm/argf.go ToS 0.0% @@ -34,6 +42,9 @@ internal/vm/csv.go ToS 0.0% internal/vm/csv.go ToS#2 0.0% internal/vm/date_bind.go Inspect 0.0% internal/vm/date_bind.go ToS 0.0% +internal/vm/defined.go definedResponds 85.7% +internal/vm/defined.go hasInlineArith 75.0% +internal/vm/defined.go respondsTo 80.0% internal/vm/encoding.go ToS 0.0% internal/vm/enumerator.go ToS#2 0.0% internal/vm/io.go inspectStr 83.3% diff --git a/scripts/coverage/BELOW100.ubuntu-latest b/scripts/coverage/BELOW100.ubuntu-latest index 172bba28..7c587c26 100644 --- a/scripts/coverage/BELOW100.ubuntu-latest +++ b/scripts/coverage/BELOW100.ubuntu-latest @@ -17,6 +17,14 @@ # is not done: that cost is not observable at program level -- five runs of # 200000 puts gave overlapping distributions -- and a second serial-validated # cache for an unobservable effect is complexity bought with nothing. +# The three defined.go rows are an operator SPECIAL CASE that fourteen new +# operator records made unreachable for Integer/Float/String/Array -- defined? +# now finds the method the ordinary way. It is NOT removed, because it still +# serves the receivers binaryOp computes on WITHOUT a record: Bignum, +# Rational, Complex, Time. Settling that needs a defined? census across every +# such receiver against ruby, which is its own measured wave (issue filed), so +# the arms stay and are recorded rather than deleted on a guess. +# # Recorded 2026-09-28 from run 36424534618, main @ 1a79138b, CI's own measurement. internal/object/object.go repr 90.9% internal/vm/argf.go ToS 0.0% @@ -34,6 +42,9 @@ internal/vm/csv.go ToS 0.0% internal/vm/csv.go ToS#2 0.0% internal/vm/date_bind.go Inspect 0.0% internal/vm/date_bind.go ToS 0.0% +internal/vm/defined.go definedResponds 85.7% +internal/vm/defined.go hasInlineArith 75.0% +internal/vm/defined.go respondsTo 80.0% internal/vm/dir.go cwdStillNamed 77.8% internal/vm/encoding.go ToS 0.0% internal/vm/enumerator.go ToS#2 0.0%