-
Notifications
You must be signed in to change notification settings - Fork 5.6k
Expand file tree
/
Copy pathprism_compile.h
More file actions
198 lines (166 loc) · 7.99 KB
/
prism_compile.h
File metadata and controls
198 lines (166 loc) · 7.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
#include "prism/prism.h"
#include "ruby/encoding.h"
/**
* the getlocal and setlocal instructions require two parameters. level is how
* many hops up the iseq stack one needs to go before finding the correct local
* table. The index is the index in that table where our variable is.
*
* Because these are always calculated and used together, we'll bind them
* together as a tuple.
*/
typedef struct pm_local_index_struct {
int index, level;
} pm_local_index_t;
// A declaration for the struct that lives in compile.c.
struct iseq_link_anchor;
/**
* A direct-indexed lookup table mapping constant IDs to local variable indices.
* Regular constant IDs (1..constants_size) index directly. Special forwarding
* parameter IDs (idMULT|FLAG, etc.) are mapped to 4 extra slots at the end.
*
* All lookups are O(1) — a single array dereference.
* The table is arena-allocated for child scopes (no explicit free needed).
*/
typedef struct {
/** Array of local indices, indexed by constant_id. -1 means not present. */
int *values;
/** Total number of slots (constants_size + PM_INDEX_LOOKUP_SPECIALS). */
int capacity;
/** Whether the values array is heap-allocated and needs explicit free. */
bool owned;
} pm_index_lookup_table_t;
/** Number of extra slots for special forwarding parameter IDs. */
#define PM_INDEX_LOOKUP_SPECIALS 4
/** Slot offsets for special forwarding parameters (relative to constants_size). */
#define PM_SPECIAL_CONSTANT_FLAG ((pm_constant_id_t) (1 << 31))
#define PM_INDEX_LOOKUP_SPECIAL_MULT 0
#define PM_INDEX_LOOKUP_SPECIAL_POW 1
#define PM_INDEX_LOOKUP_SPECIAL_AND 2
#define PM_INDEX_LOOKUP_SPECIAL_DOT3 3
/**
* Special constant IDs for forwarding parameters. These use bit 31 to
* distinguish them from regular prism constant pool IDs. The lower bits
* encode which special slot (0-3) they map to in the lookup table.
*/
#define PM_CONSTANT_MULT ((pm_constant_id_t) (PM_SPECIAL_CONSTANT_FLAG | PM_INDEX_LOOKUP_SPECIAL_MULT))
#define PM_CONSTANT_POW ((pm_constant_id_t) (PM_SPECIAL_CONSTANT_FLAG | PM_INDEX_LOOKUP_SPECIAL_POW))
#define PM_CONSTANT_AND ((pm_constant_id_t) (PM_SPECIAL_CONSTANT_FLAG | PM_INDEX_LOOKUP_SPECIAL_AND))
#define PM_CONSTANT_DOT3 ((pm_constant_id_t) (PM_SPECIAL_CONSTANT_FLAG | PM_INDEX_LOOKUP_SPECIAL_DOT3))
static inline int
pm_index_lookup_table_index(const pm_index_lookup_table_t *table, pm_constant_id_t key)
{
if (LIKELY(!(key & PM_SPECIAL_CONSTANT_FLAG))) {
return (int) key - 1;
}
return table->capacity - PM_INDEX_LOOKUP_SPECIALS + (int)(key & ~PM_SPECIAL_CONSTANT_FLAG);
}
static inline void
pm_index_lookup_table_insert(pm_index_lookup_table_t *table, pm_constant_id_t key, int value)
{
int idx = pm_index_lookup_table_index(table, key);
RUBY_ASSERT(idx >= 0 && idx < table->capacity);
table->values[idx] = value;
}
static inline int
pm_index_lookup_table_lookup(const pm_index_lookup_table_t *table, pm_constant_id_t key, int *value)
{
int idx = pm_index_lookup_table_index(table, key);
RUBY_ASSERT(idx >= 0 && idx < table->capacity);
if (table->values[idx] == -1) return 0;
*value = table->values[idx];
return 1;
}
static inline void
pm_index_lookup_table_init_heap(pm_index_lookup_table_t *table, int constants_size)
{
int cap = constants_size + PM_INDEX_LOOKUP_SPECIALS;
table->values = (int *) ruby_xmalloc(cap * sizeof(int));
memset(table->values, -1, cap * sizeof(int));
table->capacity = cap;
table->owned = true;
}
// ScopeNodes are helper nodes, and will never be part of the AST. We manually
// declare them here to avoid generating them.
typedef struct pm_scope_node {
pm_node_t base;
struct pm_scope_node *previous;
pm_node_t *ast_node;
pm_node_t *parameters;
pm_node_t *body;
pm_constant_id_list_t locals;
const pm_parser_t *parser;
const pm_options_t *options;
const pm_line_offset_list_t *line_offsets;
int32_t start_line;
rb_encoding *encoding;
/**
* This is a pointer to the list of script lines for the ISEQs that will be
* associated with this scope node. It is only set if
* RubyVM.keep_script_lines is true. If it is set, it will be set to a
* pointer to an array that is always stack allocated (so no GC marking is
* needed by this struct). If it is not set, it will be NULL. It is
* inherited by all child scopes.
*/
VALUE *script_lines;
/**
* This is the encoding of the actual filepath object that will be used when
* a __FILE__ node is compiled or when the path has to be set on a syntax
* error.
*/
rb_encoding *filepath_encoding;
// The size of the local table on the iseq which includes locals and hidden
// variables.
int local_table_for_iseq_size;
ID *constants;
/**
* A flat lookup table mapping constant IDs (or special IDs) to local
* variable indices. When allocated from the compile data arena (child
* scopes), no explicit free is needed. When heap-allocated (top-level
* scope in pm_parse_process), owned is set to true so destroy can free it.
*/
pm_index_lookup_table_t index_lookup_table;
// The current coverage setting, passed down through the various scopes.
int coverage_enabled;
/**
* This will only be set on the top-level scope node. It will contain all of
* the instructions pertaining to BEGIN{} nodes.
*/
struct iseq_link_anchor *pre_execution_anchor;
/**
* Cached line hint for line offset list lookups. Since the compiler walks
* the AST roughly in source order, consecutive lookups tend to be for
* nearby byte offsets. This avoids repeated binary searches.
*/
size_t last_line;
} pm_scope_node_t;
void pm_scope_node_init(const pm_node_t *node, pm_scope_node_t *scope, pm_scope_node_t *previous);
void pm_scope_node_destroy(pm_scope_node_t *scope_node);
typedef struct {
/** The arena allocator for AST-lifetime memory. */
pm_arena_t *arena;
/** The parser that will do the actual parsing. */
pm_parser_t *parser;
/** The options that will be passed to the parser. */
pm_options_t *options;
/** The source backing the parse (file, string, or stream). */
pm_source_t *source;
/** The resulting scope node that will hold the generated AST. */
pm_scope_node_t node;
/** Whether or not this parse result has performed its parsing yet. */
bool parsed;
} pm_parse_result_t;
void pm_parse_result_init(pm_parse_result_t *result);
VALUE pm_load_file(pm_parse_result_t *result, VALUE filepath, bool load_error);
VALUE pm_parse_file(pm_parse_result_t *result, VALUE filepath, VALUE *script_lines);
VALUE pm_load_parse_file(pm_parse_result_t *result, VALUE filepath, VALUE *script_lines);
VALUE pm_parse_string(pm_parse_result_t *result, VALUE source, VALUE filepath, VALUE *script_lines);
VALUE pm_parse_stdin(pm_parse_result_t *result);
void pm_options_version_for_current_ruby_set(pm_options_t *options);
void pm_parse_result_free(pm_parse_result_t *result);
rb_iseq_t *pm_iseq_new(pm_scope_node_t *node, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent, enum rb_iseq_type, int *error_state);
rb_iseq_t *pm_iseq_new_top(pm_scope_node_t *node, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent, int *error_state);
rb_iseq_t *pm_iseq_new_main(pm_scope_node_t *node, VALUE path, VALUE realpath, const rb_iseq_t *parent, int opt, int *error_state);
rb_iseq_t *pm_iseq_new_eval(pm_scope_node_t *node, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_iseq_t *parent, int isolated_depth, int *error_state);
rb_iseq_t *pm_iseq_new_with_opt(pm_scope_node_t *node, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_iseq_t *parent, int isolated_depth, enum rb_iseq_type, const rb_compile_option_t *option, int *error_state);
rb_iseq_t *pm_iseq_build(pm_scope_node_t *node, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_iseq_t *parent, int isolated_depth, enum rb_iseq_type, const rb_compile_option_t *option);
VALUE pm_iseq_compile_node(rb_iseq_t *iseq, pm_scope_node_t *node);