Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions ops.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,12 +132,18 @@ func (ex *executor) slowOp() error {
case opSetIndex:
return ex.newindex(op, *ex.reg(a), Int(int64(b)), *ex.reg(c))
case opSetField:
if ex.constSet(*ex.reg(a), b, *ex.reg(c)) {
return nil
}
return ex.newindex(op, *ex.reg(a), p.k[b], *ex.reg(c))
case opSetTableK:
return ex.newindex(op, *ex.reg(a), *ex.reg(b), p.k[c])
case opSetIndexK:
return ex.newindex(op, *ex.reg(a), Int(int64(b)), p.k[c])
case opSetFieldK:
if ex.constSet(*ex.reg(a), b, p.k[c]) {
return nil
}
return ex.newindex(op, *ex.reg(a), p.k[b], p.k[c])
case opNewTable:
// the sizes are hints: clamp them, so that no instruction can ask for a huge allocation
Expand Down Expand Up @@ -1403,6 +1409,37 @@ func (ex *executor) builtinIter(base, c int) (bool, error) {
return true, ex.tick() // a call ticks
}

// constSet does obj[K[kc]] = val for a SETFIELD the loop could not do inline, such as one
// that adds a field to a table just built, when the table has no metatable and the write
// needs no resize: it finds the key with the hash computed at compile time, sets it or adds
// it, and records where for the site. It reports false when newindex must do the write.
func (ex *executor) constSet(obj Value, kc int, val Value) bool {
if !obj.isObj(kindTable) || val.IsNil() {
return false
}
t := (*Table)(obj.p)
if t.meta != nil || t.flags&(tabFrozen|tabGuarded) != 0 || len(t.nodes) == 0 {
return false
}
p := ex.st.cl.p
key, h := p.k[kc], p.khash[kc]
if !key.isString() || key.n&lenMask >= 2 && *(*uint16)(key.p) == 0x5f5f {
return false // a key starting with "__" clears the table's caches in set
}
idx := t.findNode(&key, h)
if idx >= 0 {
t.nodes[idx].v = val
} else {
if (t.used+1)*4 > len(t.nodes)*3 || t.memSize() >= maxAlloc/2 {
return false // a resize, which newindex charges for
}
idx = t.insert(key, val, h)
}
learn(&p.hints[ex.aux()], idx)
ex.st.pc++ // the aux word
return true
}

// constField does R[dst] = obj[K[kc]] for GETFIELD and SELF, when obj is a table and the
// key is in it or one cached __index hop away, or obj is a string and the key is in the
// string library, with the key's compile-time hash. It learns the site's hint and reports
Expand Down
6 changes: 3 additions & 3 deletions table.go
Original file line number Diff line number Diff line change
Expand Up @@ -380,15 +380,15 @@ func (t *Table) growArray(n int) {
}
}

// insert places a new key; the caller has made room.
func (t *Table) insert(key, val Value, h uint64) {
// insert places a new key and returns its node index; the caller has made room.
func (t *Table) insert(key, val Value, h uint64) int {
mask := uint64(len(t.nodes) - 1)
for j := h; ; j++ {
nd := &t.nodes[j&mask]
if nd.k.p == nil {
nd.k, nd.v = key, val
t.used++
return
return int(j & mask)
}
}
}
Expand Down
1 change: 1 addition & 0 deletions testdata/meta.expected
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ a tail call through __call stays a tail call str:646f6e65 | true | 2 | 3 | str:6
metamethods that are not functions str:6d6574616d6574686f6473207468617420617265206e6f742066756e6374696f6e733a343a20617474656d707420746f2063616c6c206120737472696e672076616c756520286d6574616d6574686f6420276164642729 | true | str:6d6574616d6574686f6473207468617420617265206e6f742066756e6374696f6e733a363a20617474656d707420746f2063616c6c206120626f6f6c65616e2076616c756520286d6574616d6574686f6420276c656e2729 | str:6d6574616d6574686f6473207468617420617265206e6f742066756e6374696f6e733a373a20617474656d707420746f2063616c6c2061206e756d6265722076616c756520286d6574616d6574686f642027636f6e6361742729 | str:63616c6c6564 | str:6d6574616d6574686f6473207468617420617265206e6f742066756e6374696f6e733a31303a20617474656d707420746f2063616c6c2061207461626c652076616c756520286d6574616d6574686f64202765712729 | str:6d6574616d6574686f6473207468617420617265206e6f742066756e6374696f6e733a31323a20617474656d707420746f2063616c6c2061206e756d6265722076616c756520286d6574616d6574686f642027636c6f73652729 | str:6d6574616d6574686f6473207468617420617265206e6f742066756e6374696f6e733a31343a20617474656d707420746f2063616c6c2061206e696c2076616c756520286d6574616d6574686f642027636c6f73652729 | str:617474656d707420746f2063616c6c2061206e756d6265722076616c7565 | str:6e696c | str:7461626c65
__index and __newindex chains that loop false | str:5f5f696e64657820616e64205f5f6e6577696e64657820636861696e732074686174206c6f6f703a363a20275f5f696e6465782720636861696e20746f6f206c6f6e673b20706f737369626c65206c6f6f70 | false | str:5f5f696e64657820616e64205f5f6e6577696e64657820636861696e732074686174206c6f6f703a373a20275f5f6e6577696e6465782720636861696e20746f6f206c6f6e673b20706f737369626c65206c6f6f70 | false | str:275f5f696e6465782720636861696e20746f6f206c6f6e673b20706f737369626c65206c6f6f70 | false | str:275f5f6e6577696e6465782720636861696e20746f6f206c6f6e673b20706f737369626c65206c6f6f70
index errors raised by the library, called from Lua str:275f5f696e6465782720636861696e20746f6f206c6f6e673b20706f737369626c65206c6f6f70 | str:275f5f6e6577696e6465782720636861696e20746f6f206c6f6e673b20706f737369626c65206c6f6f70 | str:617474656d707420746f20696e6465782061206e756d6265722076616c7565 | str:617474656d707420746f20696e6465782061206e756d6265722076616c7565
fields added to tables just built, with and without __newindex 270 | 11 | 22 | 33 | 44 | 55 | 66 | 6 | str:7121 | 43
30 changes: 30 additions & 0 deletions testdata/meta.lua
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,33 @@ return err(function() return table.unpack(setmetatable({}, L), 1, 1) end),
err(function() table.insert(setmetatable({}, L), 1) end),
err(function() for _ in ipairs(5) do end end),
err(function() table.insert(setmetatable({}, {__newindex = 5}), 1) end)
--== fields added to tables just built, with and without __newindex
local function new(x, y) return {x = x, y = y} end
local r, sum = {}, 0
for i = 1, 20 do
local p = new(i, 2 * i)
p.z = i; p.w = -i -- adds, the second one past the size the constructor asked for
p.y = nil; p.y = 3 -- sets a deleted key again
sum = sum + p.x + p.y + p.z + p.w
end
r[#r + 1] = sum
local logged = {}
local mt = {__newindex = function(t, k, v) logged[#logged + 1] = k; rawset(t, k, v * 10) end}
for i = 1, 6 do
local p = setmetatable(new(i, i), mt)
p.x = i -- present: no __newindex
p.z = i -- absent: __newindex
r[#r + 1] = p.x + p.z
end
r[#r + 1] = #logged
local m = new(1, 2)
local o = setmetatable({}, m)
m.__index = function(_, k) return k .. "!" end -- a "__" key added to a table in use as a metatable
r[#r + 1] = o.q
local n = 0
local t = new(1, 2)
for i = 1, 40 do t["k" .. i] = i end
t.v = 0
for _ in pairs(t) do n = n + 1 end
r[#r + 1] = n
return table.unpack(r)
Loading