immunio 1.1.13 → 1.1.15
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/immunio/plugins/action_view.rb +22 -4
- data/lib/immunio/plugins/active_record.rb +6 -1
- data/lib/immunio/plugins/active_record_relation.rb +5 -5
- data/lib/immunio/plugins/exception_handler.rb +8 -2
- data/lib/immunio/version.rb +1 -1
- data/lua-hooks/Makefile +31 -11
- data/lua-hooks/ext/all.c +3 -3
- data/lua-hooks/ext/libinjection/libinjection_sqli.h +1 -1
- data/lua-hooks/ext/libinjection/lualib.c +16 -0
- data/lua-hooks/ext/lpeg/HISTORY +7 -1
- data/lua-hooks/ext/lpeg/lpcap.c +2 -2
- data/lua-hooks/ext/lpeg/lpcap.h +17 -4
- data/lua-hooks/ext/lpeg/lpcode.c +123 -95
- data/lua-hooks/ext/lpeg/lpcode.h +12 -6
- data/lua-hooks/ext/lpeg/lpeg.html +39 -23
- data/lua-hooks/ext/lpeg/lpprint.c +17 -17
- data/lua-hooks/ext/lpeg/lpprint.h +3 -2
- data/lua-hooks/ext/lpeg/lptree.c +264 -199
- data/lua-hooks/ext/lpeg/lptree.h +30 -25
- data/lua-hooks/ext/lpeg/lptypes.h +24 -24
- data/lua-hooks/ext/lpeg/lpvm.c +27 -18
- data/lua-hooks/ext/lpeg/lpvm.h +6 -6
- data/lua-hooks/ext/lpeg/re.html +5 -5
- data/lua-hooks/ext/luajit/src/lib_init.c +19 -15
- data/lua-hooks/lib/hooks/module.mk +12 -7
- data/lua-hooks/lib/hooks/xss/module.mk +2 -2
- data/lua-hooks/lib/lexers/module.mk +2 -3
- data/lua-hooks/lib/module.mk +7 -3
- data/lua-hooks/options.mk +9 -3
- metadata +6 -7
- data/lua-hooks/ext/lpeg/test.lua +0 -1409
- data/lua-hooks/lib/schema/module.mk +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7ae2946181dda203daf7cce9652ac99d78f0c822
|
4
|
+
data.tar.gz: c5c3ef8b2ab329ac11e5f3823601df36db381853
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ce62844b8ece04eaef147d59391ede1299b46734078ab3e5c1f4bf2965fa57544ed1a7439797a9cc1bda6dffe3473af2411f04d8a513ef199b8e3e2658761c54
|
7
|
+
data.tar.gz: 266a2912f823c888e4c61ed20c84b2a780592f60ad1f6b581d1bbe5587b386b99347d2bb70f21815ce357b65221c9895135751d2e72a9e11519e6575925df8fd
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# Hook into ActionView rendering to inject Immunio's hooks.
|
2
2
|
require 'securerandom'
|
3
|
+
require 'ripper'
|
3
4
|
|
4
5
|
module Immunio
|
5
6
|
# Renders templates by filtering them through Immunio's hook handlers.
|
@@ -237,6 +238,18 @@ module Immunio
|
|
237
238
|
end
|
238
239
|
end
|
239
240
|
|
241
|
+
def self.remove_comment(code)
|
242
|
+
*, last_line = code.rpartition("\n")
|
243
|
+
|
244
|
+
comment = Ripper.slice(last_line, "comment")
|
245
|
+
|
246
|
+
if comment
|
247
|
+
code = code.sub(Regexp.new(Regexp.escape(comment) + "\\Z"), "")
|
248
|
+
end
|
249
|
+
|
250
|
+
code
|
251
|
+
end
|
252
|
+
|
240
253
|
# Generate code injected in templates to wrap everything inside `<%= ... %>`.
|
241
254
|
def self.generate_render_var_code(code, escape)
|
242
255
|
template = Template.current
|
@@ -250,7 +263,7 @@ module Immunio
|
|
250
263
|
handler.class.name
|
251
264
|
end
|
252
265
|
|
253
|
-
"(__immunio_result = (#{code}); Immunio::Template.render_var(#{code.strip.inspect}, __immunio_result, #{template_id}, '#{template.template_sha}', __FILE__, __LINE__, #{escape}, #{template.is_text?}, '#{handler_name}'))"
|
266
|
+
"(__immunio_result = (#{remove_comment(code)}); Immunio::Template.render_var(#{code.strip.inspect}, __immunio_result, #{template_id}, '#{template.template_sha}', __FILE__, __LINE__, #{escape}, #{template.is_text?}, '#{handler_name}'))"
|
254
267
|
else
|
255
268
|
code
|
256
269
|
end
|
@@ -568,9 +581,14 @@ Immunio::Plugin.load(
|
|
568
581
|
|
569
582
|
ActionView::TemplateRenderer.send :include, Immunio::TemplateRendererHooks
|
570
583
|
ActionView::Template.send :include, Immunio::TemplateHooks
|
571
|
-
|
572
|
-
|
573
|
-
|
584
|
+
|
585
|
+
if Rails::VERSION::MAJOR < 5
|
586
|
+
ActionController::Caching::Fragments.send(
|
587
|
+
:include,
|
588
|
+
Immunio::FragmentCachingHooks)
|
589
|
+
else
|
590
|
+
AbstractController::Caching.send(:include, Immunio::FragmentCachingHooks)
|
591
|
+
end
|
574
592
|
|
575
593
|
plugin.loaded! Rails.version
|
576
594
|
end
|
@@ -168,7 +168,12 @@ module Immunio
|
|
168
168
|
visit o.wheres, modifier: :where
|
169
169
|
visit o.groups, modifier: :group
|
170
170
|
visit o.windows, context
|
171
|
-
|
171
|
+
|
172
|
+
if Rails::VERSION::MAJOR > 4
|
173
|
+
visit o.havings, modifier: :having
|
174
|
+
else
|
175
|
+
visit o.having, modifier: :having
|
176
|
+
end
|
172
177
|
end
|
173
178
|
|
174
179
|
def visit_Arel_Nodes_SelectStatement(o, context, _opts)
|
@@ -41,8 +41,8 @@ module Immunio
|
|
41
41
|
# Sometimes ActiveRecord creates a new relation with a new condition and
|
42
42
|
# merges it into an existing relation. I'm not sure why, and I'm not going
|
43
43
|
# to ask. Just copy the context data from the other relation into this one.
|
44
|
-
def merge_with_immunio(other)
|
45
|
-
return merge_without_immunio(other) unless other && !other.is_a?(Array)
|
44
|
+
def merge_with_immunio(other, *args)
|
45
|
+
return merge_without_immunio(other, *args) unless other && !other.is_a?(Array)
|
46
46
|
|
47
47
|
# Rails 4 added the ability to call merge with a proc, like:
|
48
48
|
#
|
@@ -51,7 +51,7 @@ module Immunio
|
|
51
51
|
# We don't need to do anything here. If the proc calls relation methods,
|
52
52
|
# they will be called on the right relation and everything will be good.
|
53
53
|
if !other.is_a?(ActiveRecord::Relation) && other.respond_to?(:to_proc)
|
54
|
-
return merge_without_immunio(other)
|
54
|
+
return merge_without_immunio(other, *args)
|
55
55
|
end
|
56
56
|
|
57
57
|
# Rails 4 added the ability to merge in a hash of conditions, like:
|
@@ -63,13 +63,13 @@ module Immunio
|
|
63
63
|
if other.is_a?(Hash)
|
64
64
|
# This shouldn't happen, but let's be safe.
|
65
65
|
unless defined? ActiveRecord::Relation::HashMerger
|
66
|
-
return merge_without_immunio(other)
|
66
|
+
return merge_without_immunio(other, *args)
|
67
67
|
end
|
68
68
|
|
69
69
|
other = ActiveRecord::Relation::HashMerger.new(self, other).other
|
70
70
|
end
|
71
71
|
|
72
|
-
spawned = merge_without_immunio(other)
|
72
|
+
spawned = merge_without_immunio(other, *args)
|
73
73
|
Request.time "plugin", "Immunio::RelationTracking" do
|
74
74
|
QueryTracker.instance.merge_relations spawned, other
|
75
75
|
end
|
@@ -46,8 +46,14 @@ module Immunio
|
|
46
46
|
|
47
47
|
# Unwrap the innermost original exception.
|
48
48
|
def unwrap_exception(e)
|
49
|
-
|
50
|
-
e
|
49
|
+
if Rails::VERSION::MAJOR > 4
|
50
|
+
while e.respond_to?(:cause) && e.cause.is_a?(Exception)
|
51
|
+
e = e.cause
|
52
|
+
end
|
53
|
+
else
|
54
|
+
while e.respond_to?(:original_exception) && e.original_exception.is_a?(Exception)
|
55
|
+
e = e.original_exception
|
56
|
+
end
|
51
57
|
end
|
52
58
|
e
|
53
59
|
end
|
data/lib/immunio/version.rb
CHANGED
data/lua-hooks/Makefile
CHANGED
@@ -10,13 +10,18 @@ CXX_SRC :=
|
|
10
10
|
|
11
11
|
SPECIAL_SRC :=
|
12
12
|
|
13
|
-
|
13
|
+
LUA_BASE_SRC :=
|
14
|
+
|
15
|
+
LUA_PROTECT_SRC :=
|
14
16
|
|
15
17
|
CLI_SRC := \
|
16
18
|
ext/luajit/src/luajit.c
|
17
19
|
|
18
|
-
# Include module definitions
|
19
|
-
|
20
|
+
# Include lib module definitions if present
|
21
|
+
# It will be missing from the agent public distribution
|
22
|
+
-include lib/module.mk
|
23
|
+
# Include extension module definitions
|
24
|
+
include ext/module.mk
|
20
25
|
|
21
26
|
OBJ := \
|
22
27
|
$(patsubst %.c,%.o,$(filter %.c,$(SRC))) \
|
@@ -41,7 +46,8 @@ A_OUT = libimmunio.a
|
|
41
46
|
include options.mk
|
42
47
|
|
43
48
|
INIT_HOOK = hooks/__init__.lua
|
44
|
-
|
49
|
+
INIT_NOPROTECT_HOOK = hooks/__init__noprotect.lua
|
50
|
+
HOOK_SRCS := $(wildcard hooks/*.lua) hooks/__init__.lua hooks/__init__noprotect.lua
|
45
51
|
MIN_SRCS = $(HOOK_SRCS:hooks/%.lua=build/%.lua)
|
46
52
|
HOOKS_TARBALL = hooks.tgz
|
47
53
|
HOOKS_SRCS_TARBALL = hooks_srcs.tgz
|
@@ -50,7 +56,7 @@ LUAJIT_OUT = libluajit.a
|
|
50
56
|
LUAJIT_OBJ = ext/luajit/src/${LUAJIT_OUT}
|
51
57
|
|
52
58
|
# Build lua, run tests, and create hooks archive
|
53
|
-
all: ${CLI} ${INIT_HOOK} ${HOOKS_TARBALL} ${HOOKS_SRCS_TARBALL}
|
59
|
+
all: ${CLI} ${INIT_HOOK} ${INIT_NOPROTECT_HOOK} ${HOOKS_TARBALL} ${HOOKS_SRCS_TARBALL}
|
54
60
|
|
55
61
|
${SO_OUT}: ${OBJ} ${SPECIAL_OBJ} ${LUAJIT_OBJ}
|
56
62
|
${CC} -shared ${CFLAGS} ${LIBS} -o $@ -lc $^
|
@@ -62,7 +68,11 @@ ${LUAJIT_OUT}: ${LUAJIT_OBJ}
|
|
62
68
|
cp $^ $@
|
63
69
|
|
64
70
|
${LUAJIT_OBJ}:
|
65
|
-
|
71
|
+
ifneq (,$(TARGET_SYS))
|
72
|
+
cd ext/luajit/src && make CROSS="${CROSS}" CC=cc HOST_CC=cc TARGET_SYS=$(TARGET_SYS) XCFLAGS="${LUAJIT_XCFLAGS}" ${LUAJIT_OUT}
|
73
|
+
else
|
74
|
+
cd ext/luajit/src && make XCFLAGS="${LUAJIT_XCFLAGS}" ${LUAJIT_OUT}
|
75
|
+
endif
|
66
76
|
|
67
77
|
# Build lua executable for testing and compilation
|
68
78
|
# Seperate compilation as we need the LUA_UNSAFE_MODE flag set...
|
@@ -71,10 +81,14 @@ ${CLI}: CXXFLAGS += ${UNSAFE_FLAG}
|
|
71
81
|
${CLI}: ${CLI_SRC} ${SRC} ${SPECIAL_OBJ} ${CXX_OBJ} ${LUAJIT_OBJ}
|
72
82
|
${CC} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LIBS}
|
73
83
|
|
74
|
-
# Concatenate init hooks into one __init__.lua
|
75
|
-
${
|
84
|
+
# Concatenate init hooks into one __init__.lua
|
85
|
+
${INIT_NOPROTECT_HOOK}: ${LUA_BASE_SRC} ${CLI}
|
86
|
+
rm -f hooks/__init__noprotect.lua
|
87
|
+
${CLI} ./luald.lua ${LUA_BASE_SRC} > hooks/__init__noprotect.lua
|
88
|
+
|
89
|
+
${INIT_HOOK}: ${LUA_BASE_SRC} ${LUA_PROTECT_SRC} ${CLI}
|
76
90
|
rm -f hooks/__init__.lua
|
77
|
-
${CLI} ./luald.lua ${
|
91
|
+
${CLI} ./luald.lua ${LUA_BASE_SRC} ${LUA_PROTECT_SRC} > hooks/__init__.lua
|
78
92
|
|
79
93
|
build/%.lua: hooks/%.lua ${CLI}
|
80
94
|
@mkdir -p build
|
@@ -100,6 +114,7 @@ ${CLI_OBJ} ${OBJ} ${CXX_OBJ} ${LUAJIT_OBJ}: CFLAGS += ${OPTIMIZE_FULL}
|
|
100
114
|
|
101
115
|
cleanhooks:
|
102
116
|
rm -f ${INIT_HOOK}
|
117
|
+
rm -f ${INIT_NOPROTECT_HOOK}
|
103
118
|
rm -f build/*.lua
|
104
119
|
|
105
120
|
clean: cleanhooks
|
@@ -109,9 +124,14 @@ clean: cleanhooks
|
|
109
124
|
rm -rf build
|
110
125
|
find . -name \*.o -delete
|
111
126
|
|
112
|
-
test: ${CLI} ${INIT_HOOK} lint ${MIN_SRCS}
|
127
|
+
test: ${CLI} ${INIT_HOOK} ${INIT_NOPROTECT_HOOK} lint ${MIN_SRCS}
|
113
128
|
@rm -f test_failed
|
114
129
|
@for file in test/*_test.lua; do \
|
130
|
+
printf "\nRunning $$file\n"; \
|
131
|
+
TEST_NOPROTECT_HOOKS=1 TEST_BUILT_HOOKS=1 ${CLI} -e 'package.path="./LuaMinify/?.lua;" .. package.path' -l 'Strict' $$file || touch test_failed; \
|
132
|
+
TEST_BUILT_HOOKS=1 ${CLI} -e 'package.path="./LuaMinify/?.lua;" .. package.path' -l 'Strict' $$file || touch test_failed; \
|
133
|
+
done
|
134
|
+
@for file in test_protect/*_test.lua; do \
|
115
135
|
printf "\nRunning $$file\n"; \
|
116
136
|
TEST_BUILT_HOOKS=1 ${CLI} -e 'package.path="./LuaMinify/?.lua;" .. package.path' -l 'Strict' $$file || touch test_failed; \
|
117
137
|
done
|
@@ -122,7 +142,7 @@ enable-console: cleanhooks
|
|
122
142
|
cp lib/term.lua.dev lib/term.lua
|
123
143
|
make
|
124
144
|
|
125
|
-
lint: ${INIT_HOOK}
|
145
|
+
lint: ${INIT_HOOK} ${INIT_NOPROTECT_HOOK}
|
126
146
|
@# Scan all lua files for lines with trailing spaces
|
127
147
|
@# The leading `!` negates the logic, so this target fails if trailing
|
128
148
|
@# spaces are found in any Lua file.
|
data/lua-hooks/ext/all.c
CHANGED
@@ -48,8 +48,9 @@ static const luaL_Reg lj_lib_load[] = {
|
|
48
48
|
{ NULL, NULL }
|
49
49
|
};
|
50
50
|
|
51
|
-
//
|
52
|
-
|
51
|
+
// Ruby agent requires these functions to be present.
|
52
|
+
// In safe mode, where they are not, we provide a noop.
|
53
|
+
#if !defined(LUA_UNSAFE_MODE) && !defined(LUA_NO_MOCK_UNSAFE)
|
53
54
|
LUALIB_API int luaopen_io(lua_State *L) {
|
54
55
|
return 0;
|
55
56
|
}
|
@@ -66,4 +67,3 @@ LUALIB_API void luaL_openlibs(lua_State *L) {
|
|
66
67
|
lua_call(L, 1, 0);
|
67
68
|
}
|
68
69
|
}
|
69
|
-
|
@@ -96,6 +96,18 @@ int sqli_tokenize(lua_State *L) {
|
|
96
96
|
lua_pushlstring(L, &state.tokenvec[0].type, 1);
|
97
97
|
lua_setfield(L, -2, "type");
|
98
98
|
|
99
|
+
if (state.tokenvec[0].str_open != '\0') {
|
100
|
+
/* Token.str_open = str_open */
|
101
|
+
lua_pushlstring(L, &state.tokenvec[0].str_open, 1);
|
102
|
+
lua_setfield(L, -2, "str_open");
|
103
|
+
}
|
104
|
+
|
105
|
+
if (state.tokenvec[0].str_close != '\0') {
|
106
|
+
/* Token.str_close = str_open */
|
107
|
+
lua_pushlstring(L, &state.tokenvec[0].str_close, 1);
|
108
|
+
lua_setfield(L, -2, "str_close");
|
109
|
+
}
|
110
|
+
|
99
111
|
/* Token.var_symbol_count = count for variable tokens */
|
100
112
|
if (state.tokenvec[0].type == 'v') {
|
101
113
|
lua_pushinteger(L, state.tokenvec[0].count);
|
@@ -107,6 +119,10 @@ int sqli_tokenize(lua_State *L) {
|
|
107
119
|
state.tokenvec[0].len);
|
108
120
|
lua_setfield(L, -2, "value");
|
109
121
|
|
122
|
+
/* Token.pos = pos */
|
123
|
+
lua_pushinteger(L, state.tokenvec[0].pos);
|
124
|
+
lua_setfield(L, -2, "pos");
|
125
|
+
|
110
126
|
/* Tokens.append(Token) */
|
111
127
|
lua_pushinteger(L, token_cnt++);
|
112
128
|
lua_insert(L, -2); /* [..., token, index] --> [..., index, token] */
|
data/lua-hooks/ext/lpeg/HISTORY
CHANGED
@@ -1,4 +1,10 @@
|
|
1
|
-
HISTORY for LPeg 0
|
1
|
+
HISTORY for LPeg 1.0
|
2
|
+
|
3
|
+
* Changes from version 0.12 to 1.0
|
4
|
+
---------------------------------
|
5
|
+
+ group "names" can be any Lua value
|
6
|
+
+ some bugs fixed
|
7
|
+
+ other small improvements
|
2
8
|
|
3
9
|
* Changes from version 0.11 to 0.12
|
4
10
|
---------------------------------
|
data/lua-hooks/ext/lpeg/lpcap.c
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
/*
|
2
|
-
** $Id: lpcap.c,v 1.
|
2
|
+
** $Id: lpcap.c,v 1.6 2015/06/15 16:09:57 roberto Exp $
|
3
3
|
** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license)
|
4
4
|
*/
|
5
5
|
|
@@ -126,7 +126,7 @@ static Capture *findback (CapState *cs, Capture *cap) {
|
|
126
126
|
continue; /* opening an enclosing capture: skip and get previous */
|
127
127
|
if (captype(cap) == Cgroup) {
|
128
128
|
getfromktable(cs, cap->idx); /* get group name */
|
129
|
-
if (
|
129
|
+
if (lp_equal(L, -2, -1)) { /* right group? */
|
130
130
|
lua_pop(L, 2); /* remove reference name and group name */
|
131
131
|
return cap;
|
132
132
|
}
|
data/lua-hooks/ext/lpeg/lpcap.h
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
/*
|
2
|
-
** $Id: lpcap.h,v 1.
|
2
|
+
** $Id: lpcap.h,v 1.3 2016/09/13 17:45:58 roberto Exp $
|
3
3
|
*/
|
4
4
|
|
5
5
|
#if !defined(lpcap_h)
|
@@ -11,14 +11,27 @@
|
|
11
11
|
|
12
12
|
/* kinds of captures */
|
13
13
|
typedef enum CapKind {
|
14
|
-
Cclose,
|
15
|
-
|
14
|
+
Cclose, /* not used in trees */
|
15
|
+
Cposition,
|
16
|
+
Cconst, /* ktable[key] is Lua constant */
|
17
|
+
Cbackref, /* ktable[key] is "name" of group to get capture */
|
18
|
+
Carg, /* 'key' is arg's number */
|
19
|
+
Csimple, /* next node is pattern */
|
20
|
+
Ctable, /* next node is pattern */
|
21
|
+
Cfunction, /* ktable[key] is function; next node is pattern */
|
22
|
+
Cquery, /* ktable[key] is table; next node is pattern */
|
23
|
+
Cstring, /* ktable[key] is string; next node is pattern */
|
24
|
+
Cnum, /* numbered capture; 'key' is number of value to return */
|
25
|
+
Csubst, /* substitution capture; next node is pattern */
|
26
|
+
Cfold, /* ktable[key] is function; next node is pattern */
|
27
|
+
Cruntime, /* not used in trees (is uses another type for tree) */
|
28
|
+
Cgroup /* ktable[key] is group's "name" */
|
16
29
|
} CapKind;
|
17
30
|
|
18
31
|
|
19
32
|
typedef struct Capture {
|
20
33
|
const char *s; /* subject position */
|
21
|
-
short idx; /* extra info
|
34
|
+
unsigned short idx; /* extra info (group name, arg index, etc.) */
|
22
35
|
byte kind; /* kind of capture */
|
23
36
|
byte siz; /* size of full capture + 1 (0 = not a full capture) */
|
24
37
|
} Capture;
|
data/lua-hooks/ext/lpeg/lpcode.c
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
/*
|
2
|
-
** $Id: lpcode.c,v 1.
|
2
|
+
** $Id: lpcode.c,v 1.24 2016/09/15 17:46:13 roberto Exp $
|
3
3
|
** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license)
|
4
4
|
*/
|
5
5
|
|
@@ -13,7 +13,7 @@
|
|
13
13
|
#include "lpcode.h"
|
14
14
|
|
15
15
|
|
16
|
-
/* signals a "no-
|
16
|
+
/* signals a "no-instruction */
|
17
17
|
#define NOINST -1
|
18
18
|
|
19
19
|
|
@@ -125,6 +125,27 @@ int tocharset (TTree *tree, Charset *cs) {
|
|
125
125
|
}
|
126
126
|
|
127
127
|
|
128
|
+
/*
|
129
|
+
** Visit a TCall node taking care to stop recursion. If node not yet
|
130
|
+
** visited, return 'f(sib2(tree))', otherwise return 'def' (default
|
131
|
+
** value)
|
132
|
+
*/
|
133
|
+
static int callrecursive (TTree *tree, int f (TTree *t), int def) {
|
134
|
+
int key = tree->key;
|
135
|
+
assert(tree->tag == TCall);
|
136
|
+
assert(sib2(tree)->tag == TRule);
|
137
|
+
if (key == 0) /* node already visited? */
|
138
|
+
return def; /* return default value */
|
139
|
+
else { /* first visit */
|
140
|
+
int result;
|
141
|
+
tree->key = 0; /* mark call as already visited */
|
142
|
+
result = f(sib2(tree)); /* go to called rule */
|
143
|
+
tree->key = key; /* restore tree */
|
144
|
+
return result;
|
145
|
+
}
|
146
|
+
}
|
147
|
+
|
148
|
+
|
128
149
|
/*
|
129
150
|
** Check whether a pattern tree has captures
|
130
151
|
*/
|
@@ -134,14 +155,17 @@ int hascaptures (TTree *tree) {
|
|
134
155
|
case TCapture: case TRunTime:
|
135
156
|
return 1;
|
136
157
|
case TCall:
|
137
|
-
|
158
|
+
return callrecursive(tree, hascaptures, 0);
|
159
|
+
case TRule: /* do not follow siblings */
|
160
|
+
tree = sib1(tree); goto tailcall;
|
138
161
|
case TOpenCall: assert(0);
|
139
162
|
default: {
|
140
163
|
switch (numsiblings[tree->tag]) {
|
141
164
|
case 1: /* return hascaptures(sib1(tree)); */
|
142
165
|
tree = sib1(tree); goto tailcall;
|
143
166
|
case 2:
|
144
|
-
if (hascaptures(sib1(tree)))
|
167
|
+
if (hascaptures(sib1(tree)))
|
168
|
+
return 1;
|
145
169
|
/* else return hascaptures(sib2(tree)); */
|
146
170
|
tree = sib2(tree); goto tailcall;
|
147
171
|
default: assert(numsiblings[tree->tag] == 0); return 0;
|
@@ -208,9 +232,9 @@ int checkaux (TTree *tree, int pred) {
|
|
208
232
|
|
209
233
|
/*
|
210
234
|
** number of characters to match a pattern (or -1 if variable)
|
211
|
-
** ('count' avoids infinite loops for grammars)
|
212
235
|
*/
|
213
|
-
int
|
236
|
+
int fixedlen (TTree *tree) {
|
237
|
+
int len = 0; /* to accumulate in tail calls */
|
214
238
|
tailcall:
|
215
239
|
switch (tree->tag) {
|
216
240
|
case TChar: case TSet: case TAny:
|
@@ -220,26 +244,29 @@ int fixedlenx (TTree *tree, int count, int len) {
|
|
220
244
|
case TRep: case TRunTime: case TOpenCall:
|
221
245
|
return -1;
|
222
246
|
case TCapture: case TRule: case TGrammar:
|
223
|
-
/* return
|
247
|
+
/* return fixedlen(sib1(tree)); */
|
224
248
|
tree = sib1(tree); goto tailcall;
|
225
|
-
case TCall:
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
249
|
+
case TCall: {
|
250
|
+
int n1 = callrecursive(tree, fixedlen, -1);
|
251
|
+
if (n1 < 0)
|
252
|
+
return -1;
|
253
|
+
else
|
254
|
+
return len + n1;
|
255
|
+
}
|
230
256
|
case TSeq: {
|
231
|
-
|
232
|
-
if (
|
233
|
-
|
234
|
-
|
257
|
+
int n1 = fixedlen(sib1(tree));
|
258
|
+
if (n1 < 0)
|
259
|
+
return -1;
|
260
|
+
/* else return fixedlen(sib2(tree)) + len; */
|
261
|
+
len += n1; tree = sib2(tree); goto tailcall;
|
235
262
|
}
|
236
263
|
case TChoice: {
|
237
|
-
int n1
|
238
|
-
|
239
|
-
if (n1 < 0)
|
240
|
-
|
241
|
-
|
242
|
-
|
264
|
+
int n1 = fixedlen(sib1(tree));
|
265
|
+
int n2 = fixedlen(sib2(tree));
|
266
|
+
if (n1 != n2 || n1 < 0)
|
267
|
+
return -1;
|
268
|
+
else
|
269
|
+
return len + n1;
|
243
270
|
}
|
244
271
|
default: assert(0); return 0;
|
245
272
|
};
|
@@ -257,7 +284,7 @@ int fixedlenx (TTree *tree, int count, int len) {
|
|
257
284
|
** pattern (full set if nothing follows it).
|
258
285
|
**
|
259
286
|
** The function returns 0 when this resulting set can be used for
|
260
|
-
** test
|
287
|
+
** test instructions that avoid the pattern altogether.
|
261
288
|
** A non-zero return can happen for two reasons:
|
262
289
|
** 1) match p '' -> '' ==> return has bit 1 set
|
263
290
|
** (tests cannot be used because they would always fail for an empty input);
|
@@ -332,7 +359,7 @@ static int getfirst (TTree *tree, const Charset *follow, Charset *firstset) {
|
|
332
359
|
}
|
333
360
|
/* else go through */
|
334
361
|
}
|
335
|
-
case TBehind: { /*
|
362
|
+
case TBehind: { /* instruction gives no new information */
|
336
363
|
/* call 'getfirst' only to check for math-time captures */
|
337
364
|
int e = getfirst(sib1(tree), follow, firstset);
|
338
365
|
loopset(i, firstset->cs[i] = follow->cs[i]); /* uses follow */
|
@@ -406,9 +433,9 @@ static int needfollow (TTree *tree) {
|
|
406
433
|
|
407
434
|
|
408
435
|
/*
|
409
|
-
** size of an
|
436
|
+
** size of an instruction
|
410
437
|
*/
|
411
|
-
int sizei (const
|
438
|
+
int sizei (const Instruction *i) {
|
412
439
|
switch((Opcode)i->i.code) {
|
413
440
|
case ISet: case ISpan: return CHARSETINSTSIZE;
|
414
441
|
case ITestSet: return CHARSETINSTSIZE + 1;
|
@@ -431,11 +458,11 @@ typedef struct CompileState {
|
|
431
458
|
|
432
459
|
|
433
460
|
/*
|
434
|
-
** code generation is recursive; 'opt' indicates that the code is
|
435
|
-
**
|
436
|
-
**
|
437
|
-
** 'tt' points to a previous test protecting this
|
438
|
-
** the follow set of the pattern.
|
461
|
+
** code generation is recursive; 'opt' indicates that the code is being
|
462
|
+
** generated as the last thing inside an optional pattern (so, if that
|
463
|
+
** code is optional too, it can reuse the 'IChoice' already in place for
|
464
|
+
** the outer pattern). 'tt' points to a previous test protecting this
|
465
|
+
** code (or NOINST). 'fl' is the follow set of the pattern.
|
439
466
|
*/
|
440
467
|
static void codegen (CompileState *compst, TTree *tree, int opt, int tt,
|
441
468
|
const Charset *fl);
|
@@ -444,16 +471,16 @@ static void codegen (CompileState *compst, TTree *tree, int opt, int tt,
|
|
444
471
|
void realloccode (lua_State *L, Pattern *p, int nsize) {
|
445
472
|
void *ud;
|
446
473
|
lua_Alloc f = lua_getallocf(L, &ud);
|
447
|
-
void *newblock = f(ud, p->code, p->codesize * sizeof(
|
448
|
-
nsize * sizeof(
|
474
|
+
void *newblock = f(ud, p->code, p->codesize * sizeof(Instruction),
|
475
|
+
nsize * sizeof(Instruction));
|
449
476
|
if (newblock == NULL && nsize > 0)
|
450
477
|
luaL_error(L, "not enough memory");
|
451
|
-
p->code = (
|
478
|
+
p->code = (Instruction *)newblock;
|
452
479
|
p->codesize = nsize;
|
453
480
|
}
|
454
481
|
|
455
482
|
|
456
|
-
static int
|
483
|
+
static int nextinstruction (CompileState *compst) {
|
457
484
|
int size = compst->p->codesize;
|
458
485
|
if (compst->ncode >= size)
|
459
486
|
realloccode(compst->L, compst->p, size * 2);
|
@@ -464,8 +491,8 @@ static int nextLpegInstruction (CompileState *compst) {
|
|
464
491
|
#define getinstr(cs,i) ((cs)->p->code[i])
|
465
492
|
|
466
493
|
|
467
|
-
static int
|
468
|
-
int i =
|
494
|
+
static int addinstruction (CompileState *compst, Opcode op, int aux) {
|
495
|
+
int i = nextinstruction(compst);
|
469
496
|
getinstr(compst, i).i.code = op;
|
470
497
|
getinstr(compst, i).i.aux = aux;
|
471
498
|
return i;
|
@@ -473,33 +500,33 @@ static int addLpegInstruction (CompileState *compst, Opcode op, int aux) {
|
|
473
500
|
|
474
501
|
|
475
502
|
/*
|
476
|
-
** Add an
|
503
|
+
** Add an instruction followed by space for an offset (to be set later)
|
477
504
|
*/
|
478
505
|
static int addoffsetinst (CompileState *compst, Opcode op) {
|
479
|
-
int i =
|
480
|
-
|
506
|
+
int i = addinstruction(compst, op, 0); /* instruction */
|
507
|
+
addinstruction(compst, (Opcode)0, 0); /* open space for offset */
|
481
508
|
assert(op == ITestSet || sizei(&getinstr(compst, i)) == 2);
|
482
509
|
return i;
|
483
510
|
}
|
484
511
|
|
485
512
|
|
486
513
|
/*
|
487
|
-
** Set the offset of an
|
514
|
+
** Set the offset of an instruction
|
488
515
|
*/
|
489
|
-
static void setoffset (CompileState *compst, int
|
490
|
-
getinstr(compst,
|
516
|
+
static void setoffset (CompileState *compst, int instruction, int offset) {
|
517
|
+
getinstr(compst, instruction + 1).offset = offset;
|
491
518
|
}
|
492
519
|
|
493
520
|
|
494
521
|
/*
|
495
|
-
** Add a capture
|
496
|
-
** 'op' is the capture
|
522
|
+
** Add a capture instruction:
|
523
|
+
** 'op' is the capture instruction; 'cap' the capture kind;
|
497
524
|
** 'key' the key into ktable; 'aux' is the optional capture offset
|
498
525
|
**
|
499
526
|
*/
|
500
527
|
static int addinstcap (CompileState *compst, Opcode op, int cap, int key,
|
501
528
|
int aux) {
|
502
|
-
int i =
|
529
|
+
int i = addinstruction(compst, op, joinkindoff(cap, aux));
|
503
530
|
getinstr(compst, i).i.key = key;
|
504
531
|
return i;
|
505
532
|
}
|
@@ -511,43 +538,43 @@ static int addinstcap (CompileState *compst, Opcode op, int cap, int key,
|
|
511
538
|
|
512
539
|
|
513
540
|
/*
|
514
|
-
** Patch '
|
541
|
+
** Patch 'instruction' to jump to 'target'
|
515
542
|
*/
|
516
|
-
static void jumptothere (CompileState *compst, int
|
517
|
-
if (
|
518
|
-
setoffset(compst,
|
543
|
+
static void jumptothere (CompileState *compst, int instruction, int target) {
|
544
|
+
if (instruction >= 0)
|
545
|
+
setoffset(compst, instruction, target - instruction);
|
519
546
|
}
|
520
547
|
|
521
548
|
|
522
549
|
/*
|
523
|
-
** Patch '
|
550
|
+
** Patch 'instruction' to jump to current position
|
524
551
|
*/
|
525
|
-
static void jumptohere (CompileState *compst, int
|
526
|
-
jumptothere(compst,
|
552
|
+
static void jumptohere (CompileState *compst, int instruction) {
|
553
|
+
jumptothere(compst, instruction, gethere(compst));
|
527
554
|
}
|
528
555
|
|
529
556
|
|
530
557
|
/*
|
531
|
-
** Code an IChar
|
558
|
+
** Code an IChar instruction, or IAny if there is an equivalent
|
532
559
|
** test dominating it
|
533
560
|
*/
|
534
561
|
static void codechar (CompileState *compst, int c, int tt) {
|
535
562
|
if (tt >= 0 && getinstr(compst, tt).i.code == ITestChar &&
|
536
563
|
getinstr(compst, tt).i.aux == c)
|
537
|
-
|
564
|
+
addinstruction(compst, IAny, 0);
|
538
565
|
else
|
539
|
-
|
566
|
+
addinstruction(compst, IChar, c);
|
540
567
|
}
|
541
568
|
|
542
569
|
|
543
570
|
/*
|
544
|
-
** Add a charset posfix to an
|
571
|
+
** Add a charset posfix to an instruction
|
545
572
|
*/
|
546
573
|
static void addcharset (CompileState *compst, const byte *cs) {
|
547
574
|
int p = gethere(compst);
|
548
575
|
int i;
|
549
576
|
for (i = 0; i < (int)CHARSETINSTSIZE - 1; i++)
|
550
|
-
|
577
|
+
nextinstruction(compst); /* space for buffer */
|
551
578
|
/* fill buffer with charset */
|
552
579
|
loopset(j, getinstr(compst, p).buff[j] = cs[j]);
|
553
580
|
}
|
@@ -556,7 +583,7 @@ static void addcharset (CompileState *compst, const byte *cs) {
|
|
556
583
|
/*
|
557
584
|
** code a char set, optimizing unit sets for IChar, "complete"
|
558
585
|
** sets for IAny, and empty sets for IFail; also use an IAny
|
559
|
-
** when
|
586
|
+
** when instruction is dominated by an equivalent test.
|
560
587
|
*/
|
561
588
|
static void codecharset (CompileState *compst, const byte *cs, int tt) {
|
562
589
|
int c = 0; /* (=) to avoid warnings */
|
@@ -566,14 +593,14 @@ static void codecharset (CompileState *compst, const byte *cs, int tt) {
|
|
566
593
|
case ISet: { /* non-trivial set? */
|
567
594
|
if (tt >= 0 && getinstr(compst, tt).i.code == ITestSet &&
|
568
595
|
cs_equal(cs, getinstr(compst, tt + 2).buff))
|
569
|
-
|
596
|
+
addinstruction(compst, IAny, 0);
|
570
597
|
else {
|
571
|
-
|
598
|
+
addinstruction(compst, ISet, 0);
|
572
599
|
addcharset(compst, cs);
|
573
600
|
}
|
574
601
|
break;
|
575
602
|
}
|
576
|
-
default:
|
603
|
+
default: addinstruction(compst, op, c); break;
|
577
604
|
}
|
578
605
|
}
|
579
606
|
|
@@ -582,7 +609,7 @@ static void codecharset (CompileState *compst, const byte *cs, int tt) {
|
|
582
609
|
** code a test set, optimizing unit sets for ITestChar, "complete"
|
583
610
|
** sets for ITestAny, and empty sets for IJmp (always fails).
|
584
611
|
** 'e' is true iff test should accept the empty string. (Test
|
585
|
-
**
|
612
|
+
** instructions in the current VM never accept the empty string.)
|
586
613
|
*/
|
587
614
|
static int codetestset (CompileState *compst, Charset *cs, int e) {
|
588
615
|
if (e) return NOINST; /* no test */
|
@@ -611,7 +638,7 @@ static int codetestset (CompileState *compst, Charset *cs, int e) {
|
|
611
638
|
/*
|
612
639
|
** Find the final destination of a sequence of jumps
|
613
640
|
*/
|
614
|
-
static int finaltarget (
|
641
|
+
static int finaltarget (Instruction *code, int i) {
|
615
642
|
while (code[i].i.code == IJmp)
|
616
643
|
i = target(code, i);
|
617
644
|
return i;
|
@@ -621,7 +648,7 @@ static int finaltarget (LpegInstruction *code, int i) {
|
|
621
648
|
/*
|
622
649
|
** final label (after traversing any jumps)
|
623
650
|
*/
|
624
|
-
static int finallabel (
|
651
|
+
static int finallabel (Instruction *code, int i) {
|
625
652
|
return finaltarget(code, target(code, i));
|
626
653
|
}
|
627
654
|
|
@@ -631,20 +658,20 @@ static int finallabel (LpegInstruction *code, int i) {
|
|
631
658
|
*/
|
632
659
|
static void codebehind (CompileState *compst, TTree *tree) {
|
633
660
|
if (tree->u.n > 0)
|
634
|
-
|
661
|
+
addinstruction(compst, IBehind, tree->u.n);
|
635
662
|
codegen(compst, sib1(tree), 0, NOINST, fullset);
|
636
663
|
}
|
637
664
|
|
638
665
|
|
639
666
|
/*
|
640
667
|
** Choice; optimizations:
|
641
|
-
** - when p1 is headfail
|
642
|
-
**
|
668
|
+
** - when p1 is headfail or
|
669
|
+
** when first(p1) and first(p2) are disjoint, than
|
643
670
|
** a character not in first(p1) cannot go to p1, and a character
|
644
671
|
** in first(p1) cannot go to p2 (at it is not in first(p2)).
|
645
672
|
** (The optimization is not valid if p1 accepts the empty string,
|
646
673
|
** as then there is no character at all...)
|
647
|
-
** - when p2 is empty and opt is true; a IPartialCommit can
|
674
|
+
** - when p2 is empty and opt is true; a IPartialCommit can reuse
|
648
675
|
** the Choice already active in the stack.
|
649
676
|
*/
|
650
677
|
static void codechoice (CompileState *compst, TTree *p1, TTree *p2, int opt,
|
@@ -671,7 +698,7 @@ static void codechoice (CompileState *compst, TTree *p1, TTree *p2, int opt,
|
|
671
698
|
}
|
672
699
|
else {
|
673
700
|
/* <p1 / p2> ==
|
674
|
-
test(
|
701
|
+
test(first(p1)) -> L1; choice L1; <p1>; commit L2; L1: <p2>; L2: */
|
675
702
|
int pcommit;
|
676
703
|
int test = codetestset(compst, &cs1, e1);
|
677
704
|
int pchoice = addoffsetinst(compst, IChoice);
|
@@ -695,7 +722,7 @@ static void codeand (CompileState *compst, TTree *tree, int tt) {
|
|
695
722
|
if (n >= 0 && n <= MAXBEHIND && !hascaptures(tree)) {
|
696
723
|
codegen(compst, tree, 0, tt, fullset);
|
697
724
|
if (n > 0)
|
698
|
-
|
725
|
+
addinstruction(compst, IBehind, n);
|
699
726
|
}
|
700
727
|
else { /* default: Choice L1; p1; BackCommit L2; L1: Fail; L2: */
|
701
728
|
int pcommit;
|
@@ -703,16 +730,17 @@ static void codeand (CompileState *compst, TTree *tree, int tt) {
|
|
703
730
|
codegen(compst, tree, 0, tt, fullset);
|
704
731
|
pcommit = addoffsetinst(compst, IBackCommit);
|
705
732
|
jumptohere(compst, pchoice);
|
706
|
-
|
733
|
+
addinstruction(compst, IFail, 0);
|
707
734
|
jumptohere(compst, pcommit);
|
708
735
|
}
|
709
736
|
}
|
710
737
|
|
711
738
|
|
712
739
|
/*
|
713
|
-
** Captures: if pattern has fixed (and not too big) length,
|
714
|
-
**
|
715
|
-
** enclose the pattern with OpenCapture -
|
740
|
+
** Captures: if pattern has fixed (and not too big) length, and it
|
741
|
+
** has no nested captures, use a single IFullCapture instruction
|
742
|
+
** after the match; otherwise, enclose the pattern with OpenCapture -
|
743
|
+
** CloseCapture.
|
716
744
|
*/
|
717
745
|
static void codecapture (CompileState *compst, TTree *tree, int tt,
|
718
746
|
const Charset *fl) {
|
@@ -738,7 +766,7 @@ static void coderuntime (CompileState *compst, TTree *tree, int tt) {
|
|
738
766
|
|
739
767
|
/*
|
740
768
|
** Repetion; optimizations:
|
741
|
-
** When pattern is a charset, can use special
|
769
|
+
** When pattern is a charset, can use special instruction ISpan.
|
742
770
|
** When pattern is head fail, or if it starts with characters that
|
743
771
|
** are disjoint from what follows the repetions, a simple test
|
744
772
|
** is enough (a fail inside the repetition would backtrack to fail
|
@@ -750,7 +778,7 @@ static void coderep (CompileState *compst, TTree *tree, int opt,
|
|
750
778
|
const Charset *fl) {
|
751
779
|
Charset st;
|
752
780
|
if (tocharset(tree, &st)) {
|
753
|
-
|
781
|
+
addinstruction(compst, ISpan, 0);
|
754
782
|
addcharset(compst, st.cs);
|
755
783
|
}
|
756
784
|
else {
|
@@ -759,7 +787,7 @@ static void coderep (CompileState *compst, TTree *tree, int opt,
|
|
759
787
|
/* L1: test (fail(p1)) -> L2; <p>; jmp L1; L2: */
|
760
788
|
int jmp;
|
761
789
|
int test = codetestset(compst, &st, 0);
|
762
|
-
codegen(compst, tree,
|
790
|
+
codegen(compst, tree, 0, test, fullset);
|
763
791
|
jmp = addoffsetinst(compst, IJmp);
|
764
792
|
jumptohere(compst, test);
|
765
793
|
jumptothere(compst, jmp, test);
|
@@ -797,12 +825,12 @@ static void codenot (CompileState *compst, TTree *tree) {
|
|
797
825
|
int e = getfirst(tree, fullset, &st);
|
798
826
|
int test = codetestset(compst, &st, e);
|
799
827
|
if (headfail(tree)) /* test (fail(p1)) -> L1; fail; L1: */
|
800
|
-
|
828
|
+
addinstruction(compst, IFail, 0);
|
801
829
|
else {
|
802
830
|
/* test(fail(p))-> L1; choice L1; <p>; failtwice; L1: */
|
803
831
|
int pchoice = addoffsetinst(compst, IChoice);
|
804
832
|
codegen(compst, tree, 0, NOINST, fullset);
|
805
|
-
|
833
|
+
addinstruction(compst, IFailTwice, 0);
|
806
834
|
jumptohere(compst, pchoice);
|
807
835
|
}
|
808
836
|
jumptohere(compst, test);
|
@@ -816,7 +844,7 @@ static void codenot (CompileState *compst, TTree *tree) {
|
|
816
844
|
static void correctcalls (CompileState *compst, int *positions,
|
817
845
|
int from, int to) {
|
818
846
|
int i;
|
819
|
-
|
847
|
+
Instruction *code = compst->p->code;
|
820
848
|
for (i = from; i < to; i += sizei(&code[i])) {
|
821
849
|
if (code[i].i.code == IOpenCall) {
|
822
850
|
int n = code[i].i.key; /* rule number */
|
@@ -848,7 +876,7 @@ static void codegrammar (CompileState *compst, TTree *grammar) {
|
|
848
876
|
for (rule = sib1(grammar); rule->tag == TRule; rule = sib2(rule)) {
|
849
877
|
positions[rulenumber++] = gethere(compst); /* save rule position */
|
850
878
|
codegen(compst, sib1(rule), 0, NOINST, fullset); /* code rule */
|
851
|
-
|
879
|
+
addinstruction(compst, IRet, 0);
|
852
880
|
}
|
853
881
|
assert(rule->tag == TTrue);
|
854
882
|
jumptohere(compst, jumptoend);
|
@@ -893,10 +921,10 @@ static void codegen (CompileState *compst, TTree *tree, int opt, int tt,
|
|
893
921
|
tailcall:
|
894
922
|
switch (tree->tag) {
|
895
923
|
case TChar: codechar(compst, tree->u.n, tt); break;
|
896
|
-
case TAny:
|
924
|
+
case TAny: addinstruction(compst, IAny, 0); break;
|
897
925
|
case TSet: codecharset(compst, treebuffer(tree), tt); break;
|
898
926
|
case TTrue: break;
|
899
|
-
case TFalse:
|
927
|
+
case TFalse: addinstruction(compst, IFail, 0); break;
|
900
928
|
case TChoice: codechoice(compst, sib1(tree), sib2(tree), opt, fl); break;
|
901
929
|
case TRep: coderep(compst, sib1(tree), opt, fl); break;
|
902
930
|
case TBehind: codebehind(compst, tree); break;
|
@@ -917,23 +945,23 @@ static void codegen (CompileState *compst, TTree *tree, int opt, int tt,
|
|
917
945
|
|
918
946
|
|
919
947
|
/*
|
920
|
-
** Optimize jumps and other jump-like
|
921
|
-
** * Update labels of
|
948
|
+
** Optimize jumps and other jump-like instructions.
|
949
|
+
** * Update labels of instructions with labels to their final
|
922
950
|
** destinations (e.g., choice L1; ... L1: jmp L2: becomes
|
923
951
|
** choice L2)
|
924
|
-
** * Jumps to other
|
925
|
-
**
|
952
|
+
** * Jumps to other instructions that do jumps become those
|
953
|
+
** instructions (e.g., jump to return becomes a return; jump
|
926
954
|
** to commit becomes a commit)
|
927
955
|
*/
|
928
956
|
static void peephole (CompileState *compst) {
|
929
|
-
|
957
|
+
Instruction *code = compst->p->code;
|
930
958
|
int i;
|
931
959
|
for (i = 0; i < compst->ncode; i += sizei(&code[i])) {
|
932
960
|
redo:
|
933
961
|
switch (code[i].i.code) {
|
934
962
|
case IChoice: case ICall: case ICommit: case IPartialCommit:
|
935
963
|
case IBackCommit: case ITestChar: case ITestSet:
|
936
|
-
case ITestAny: { /*
|
964
|
+
case ITestAny: { /* instructions with labels */
|
937
965
|
jumptothere(compst, i, finallabel(code, i)); /* optimize label */
|
938
966
|
break;
|
939
967
|
}
|
@@ -941,15 +969,15 @@ static void peephole (CompileState *compst) {
|
|
941
969
|
int ft = finaltarget(code, i);
|
942
970
|
switch (code[ft].i.code) { /* jumping to what? */
|
943
971
|
case IRet: case IFail: case IFailTwice:
|
944
|
-
case IEnd: { /*
|
945
|
-
code[i] = code[ft]; /* jump becomes that
|
972
|
+
case IEnd: { /* instructions with unconditional implicit jumps */
|
973
|
+
code[i] = code[ft]; /* jump becomes that instruction */
|
946
974
|
code[i + 1].i.code = IAny; /* 'no-op' for target position */
|
947
975
|
break;
|
948
976
|
}
|
949
977
|
case ICommit: case IPartialCommit:
|
950
978
|
case IBackCommit: { /* inst. with unconditional explicit jumps */
|
951
979
|
int fft = finallabel(code, ft);
|
952
|
-
code[i] = code[ft]; /* jump becomes that
|
980
|
+
code[i] = code[ft]; /* jump becomes that instruction... */
|
953
981
|
jumptothere(compst, i, fft); /* but must correct its offset */
|
954
982
|
goto redo; /* reoptimize its label */
|
955
983
|
}
|
@@ -970,12 +998,12 @@ static void peephole (CompileState *compst) {
|
|
970
998
|
/*
|
971
999
|
** Compile a pattern
|
972
1000
|
*/
|
973
|
-
|
1001
|
+
Instruction *compile (lua_State *L, Pattern *p) {
|
974
1002
|
CompileState compst;
|
975
1003
|
compst.p = p; compst.ncode = 0; compst.L = L;
|
976
1004
|
realloccode(L, p, 2); /* minimum initial size */
|
977
1005
|
codegen(&compst, p->tree, 0, NOINST, fullset);
|
978
|
-
|
1006
|
+
addinstruction(&compst, IEnd, 0);
|
979
1007
|
realloccode(L, p, compst.ncode); /* set final size */
|
980
1008
|
peephole(&compst);
|
981
1009
|
return p->code;
|