pygments.rb 0.2.4 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/pygments/version.rb +1 -1
- data/vendor/pygments-main/AUTHORS +14 -0
- data/vendor/pygments-main/CHANGES +34 -3
- data/vendor/pygments-main/Makefile +1 -1
- data/vendor/pygments-main/docs/generate.py +1 -1
- data/vendor/pygments-main/external/markdown-processor.py +1 -1
- data/vendor/pygments-main/external/moin-parser.py +1 -1
- data/vendor/pygments-main/external/rst-directive-old.py +1 -1
- data/vendor/pygments-main/external/rst-directive.py +1 -1
- data/vendor/pygments-main/pygments/__init__.py +1 -1
- data/vendor/pygments-main/pygments/cmdline.py +4 -1
- data/vendor/pygments-main/pygments/console.py +1 -1
- data/vendor/pygments-main/pygments/filter.py +1 -1
- data/vendor/pygments-main/pygments/filters/__init__.py +1 -1
- data/vendor/pygments-main/pygments/formatter.py +1 -1
- data/vendor/pygments-main/pygments/formatters/__init__.py +1 -1
- data/vendor/pygments-main/pygments/formatters/_mapping.py +1 -1
- data/vendor/pygments-main/pygments/formatters/bbcode.py +1 -1
- data/vendor/pygments-main/pygments/formatters/html.py +2 -2
- data/vendor/pygments-main/pygments/formatters/img.py +1 -1
- data/vendor/pygments-main/pygments/formatters/latex.py +3 -2
- data/vendor/pygments-main/pygments/formatters/other.py +1 -1
- data/vendor/pygments-main/pygments/formatters/rtf.py +1 -1
- data/vendor/pygments-main/pygments/formatters/svg.py +1 -1
- data/vendor/pygments-main/pygments/formatters/terminal.py +5 -2
- data/vendor/pygments-main/pygments/formatters/terminal256.py +5 -2
- data/vendor/pygments-main/pygments/lexer.py +29 -10
- data/vendor/pygments-main/pygments/lexers/__init__.py +14 -11
- data/vendor/pygments-main/pygments/lexers/_asybuiltins.py +1 -1
- data/vendor/pygments-main/pygments/lexers/_clbuiltins.py +1 -1
- data/vendor/pygments-main/pygments/lexers/_luabuiltins.py +1 -1
- data/vendor/pygments-main/pygments/lexers/_mapping.py +41 -23
- data/vendor/pygments-main/pygments/lexers/_phpbuiltins.py +1 -1
- data/vendor/pygments-main/pygments/lexers/_postgres_builtins.py +1 -1
- data/vendor/pygments-main/pygments/lexers/_scilab_builtins.py +29 -0
- data/vendor/pygments-main/pygments/lexers/_vimbuiltins.py +3 -3
- data/vendor/pygments-main/pygments/lexers/agile.py +148 -443
- data/vendor/pygments-main/pygments/lexers/asm.py +5 -3
- data/vendor/pygments-main/pygments/lexers/compiled.py +298 -294
- data/vendor/pygments-main/pygments/lexers/dotnet.py +40 -34
- data/vendor/pygments-main/pygments/lexers/functional.py +723 -4
- data/vendor/pygments-main/pygments/lexers/hdl.py +228 -6
- data/vendor/pygments-main/pygments/lexers/jvm.py +678 -0
- data/vendor/pygments-main/pygments/lexers/math.py +65 -2
- data/vendor/pygments-main/pygments/lexers/other.py +875 -481
- data/vendor/pygments-main/pygments/lexers/parsers.py +1 -1
- data/vendor/pygments-main/pygments/lexers/shell.py +360 -0
- data/vendor/pygments-main/pygments/lexers/special.py +1 -1
- data/vendor/pygments-main/pygments/lexers/sql.py +565 -0
- data/vendor/pygments-main/pygments/lexers/templates.py +1 -1
- data/vendor/pygments-main/pygments/lexers/text.py +237 -100
- data/vendor/pygments-main/pygments/lexers/web.py +146 -10
- data/vendor/pygments-main/pygments/plugin.py +1 -1
- data/vendor/pygments-main/pygments/scanner.py +1 -1
- data/vendor/pygments-main/pygments/style.py +1 -1
- data/vendor/pygments-main/pygments/styles/__init__.py +2 -1
- data/vendor/pygments-main/pygments/styles/autumn.py +1 -1
- data/vendor/pygments-main/pygments/styles/borland.py +1 -1
- data/vendor/pygments-main/pygments/styles/bw.py +1 -1
- data/vendor/pygments-main/pygments/styles/colorful.py +1 -1
- data/vendor/pygments-main/pygments/styles/default.py +1 -1
- data/vendor/pygments-main/pygments/styles/emacs.py +1 -1
- data/vendor/pygments-main/pygments/styles/friendly.py +1 -1
- data/vendor/pygments-main/pygments/styles/fruity.py +1 -2
- data/vendor/pygments-main/pygments/styles/manni.py +1 -1
- data/vendor/pygments-main/pygments/styles/monokai.py +1 -1
- data/vendor/pygments-main/pygments/styles/murphy.py +1 -1
- data/vendor/pygments-main/pygments/styles/native.py +1 -1
- data/vendor/pygments-main/pygments/styles/pastie.py +1 -1
- data/vendor/pygments-main/pygments/styles/perldoc.py +1 -1
- data/vendor/pygments-main/pygments/styles/rrt.py +33 -0
- data/vendor/pygments-main/pygments/styles/tango.py +1 -1
- data/vendor/pygments-main/pygments/styles/trac.py +1 -1
- data/vendor/pygments-main/pygments/styles/vim.py +1 -1
- data/vendor/pygments-main/pygments/styles/vs.py +1 -1
- data/vendor/pygments-main/pygments/token.py +1 -1
- data/vendor/pygments-main/pygments/unistring.py +1 -1
- data/vendor/pygments-main/pygments/util.py +2 -2
- data/vendor/pygments-main/scripts/check_sources.py +2 -2
- data/vendor/pygments-main/scripts/find_codetags.py +1 -1
- data/vendor/pygments-main/scripts/find_error.py +5 -2
- data/vendor/pygments-main/scripts/get_vimkw.py +9 -4
- data/vendor/pygments-main/setup.py +1 -1
- data/vendor/pygments-main/tests/examplefiles/classes.dylan +16 -0
- data/vendor/pygments-main/tests/examplefiles/coq_RelationClasses +447 -0
- data/vendor/pygments-main/tests/examplefiles/example.cls +15 -0
- data/vendor/pygments-main/tests/examplefiles/example.moon +629 -0
- data/vendor/pygments-main/tests/examplefiles/example.p +34 -0
- data/vendor/pygments-main/tests/examplefiles/example.snobol +15 -0
- data/vendor/pygments-main/tests/examplefiles/example.u +548 -0
- data/vendor/pygments-main/tests/examplefiles/example_elixir.ex +363 -0
- data/vendor/pygments-main/tests/examplefiles/foo.sce +6 -0
- data/vendor/pygments-main/tests/examplefiles/http_request_example +14 -0
- data/vendor/pygments-main/tests/examplefiles/http_response_example +27 -0
- data/vendor/pygments-main/tests/examplefiles/irc.lsp +214 -0
- data/vendor/pygments-main/tests/examplefiles/markdown.lsp +679 -0
- data/vendor/pygments-main/tests/examplefiles/nemerle_sample.n +4 -2
- data/vendor/pygments-main/tests/examplefiles/reversi.lsp +427 -0
- data/vendor/pygments-main/tests/examplefiles/scilab.sci +30 -0
- data/vendor/pygments-main/tests/examplefiles/test.bro +250 -0
- data/vendor/pygments-main/tests/examplefiles/test.cs +23 -0
- data/vendor/pygments-main/tests/examplefiles/test.dart +23 -0
- data/vendor/pygments-main/tests/examplefiles/test.ecl +58 -0
- data/vendor/pygments-main/tests/examplefiles/test.fan +818 -0
- data/vendor/pygments-main/tests/examplefiles/test.ps1 +108 -0
- data/vendor/pygments-main/tests/examplefiles/test.vhdl +161 -0
- data/vendor/pygments-main/tests/old_run.py +1 -1
- data/vendor/pygments-main/tests/run.py +1 -1
- data/vendor/pygments-main/tests/test_basic_api.py +4 -3
- data/vendor/pygments-main/tests/test_clexer.py +1 -1
- data/vendor/pygments-main/tests/test_cmdline.py +1 -1
- data/vendor/pygments-main/tests/test_examplefiles.py +4 -3
- data/vendor/pygments-main/tests/test_html_formatter.py +33 -1
- data/vendor/pygments-main/tests/test_latex_formatter.py +1 -1
- data/vendor/pygments-main/tests/test_perllexer.py +137 -0
- data/vendor/pygments-main/tests/test_regexlexer.py +1 -1
- data/vendor/pygments-main/tests/test_token.py +1 -1
- data/vendor/pygments-main/tests/test_using_api.py +1 -1
- data/vendor/pygments-main/tests/test_util.py +35 -5
- metadata +30 -4
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Scilab ( http://www.scilab.org/ )
|
|
2
|
+
// Copyright (C) INRIA - Serge STEER
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
function I=sub2ind(dims,varargin)
|
|
6
|
+
//sub2ind is used to determine the equivalent single index
|
|
7
|
+
//corresponding to a given set of subscript values.
|
|
8
|
+
|
|
9
|
+
//I = sub2ind(dims,i1,i2,..) returns the linear index equivalent to the
|
|
10
|
+
//row, column, ... subscripts in the arrays i1,i2,.. for an matrix of
|
|
11
|
+
//size dims.
|
|
12
|
+
|
|
13
|
+
//I = sub2ind(dims,Mi) returns the linear index
|
|
14
|
+
//equivalent to the n subscripts in the columns of the matrix Mi for a matrix
|
|
15
|
+
//of size dims.
|
|
16
|
+
|
|
17
|
+
d=[1;cumprod(matrix(dims(1:$-1),-1,1))]
|
|
18
|
+
for i=1:size(varargin)
|
|
19
|
+
if varargin(i)==[] then I=[],return,end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
if size(varargin)==1 then //subindices are the columns of the argument
|
|
23
|
+
I=(varargin(1)-1)*d+1
|
|
24
|
+
else //subindices are given as separated arguments
|
|
25
|
+
I=1
|
|
26
|
+
for i=1:size(varargin)
|
|
27
|
+
I=I+(varargin(i)-1)*d(i)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
endfunction
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
@load notice
|
|
2
|
+
@load utils/thresholds
|
|
3
|
+
|
|
4
|
+
module SSH;
|
|
5
|
+
|
|
6
|
+
export {
|
|
7
|
+
redef enum Log::ID += { SSH };
|
|
8
|
+
|
|
9
|
+
redef enum Notice::Type += {
|
|
10
|
+
Login,
|
|
11
|
+
Password_Guessing,
|
|
12
|
+
Login_By_Password_Guesser,
|
|
13
|
+
Login_From_Interesting_Hostname,
|
|
14
|
+
Bytecount_Inconsistency,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type Info: record {
|
|
18
|
+
ts: time &log;
|
|
19
|
+
uid: string &log;
|
|
20
|
+
id: conn_id &log;
|
|
21
|
+
status: string &log &optional;
|
|
22
|
+
direction: string &log &optional;
|
|
23
|
+
remote_location: geo_location &log &optional;
|
|
24
|
+
client: string &log &optional;
|
|
25
|
+
server: string &log &optional;
|
|
26
|
+
resp_size: count &log &default=0;
|
|
27
|
+
|
|
28
|
+
## Indicate if the SSH session is done being watched.
|
|
29
|
+
done: bool &default=F;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const password_guesses_limit = 30 &redef;
|
|
33
|
+
|
|
34
|
+
# The size in bytes at which the SSH connection is presumed to be
|
|
35
|
+
# successful.
|
|
36
|
+
const authentication_data_size = 5500 &redef;
|
|
37
|
+
|
|
38
|
+
# The amount of time to remember presumed non-successful logins to build
|
|
39
|
+
# model of a password guesser.
|
|
40
|
+
const guessing_timeout = 30 mins &redef;
|
|
41
|
+
|
|
42
|
+
# The set of countries for which you'd like to throw notices upon successful login
|
|
43
|
+
# requires Bro compiled with libGeoIP support
|
|
44
|
+
const watched_countries: set[string] = {"RO"} &redef;
|
|
45
|
+
|
|
46
|
+
# Strange/bad host names to originate successful SSH logins
|
|
47
|
+
const interesting_hostnames =
|
|
48
|
+
/^d?ns[0-9]*\./ |
|
|
49
|
+
/^smtp[0-9]*\./ |
|
|
50
|
+
/^mail[0-9]*\./ |
|
|
51
|
+
/^pop[0-9]*\./ |
|
|
52
|
+
/^imap[0-9]*\./ |
|
|
53
|
+
/^www[0-9]*\./ |
|
|
54
|
+
/^ftp[0-9]*\./ &redef;
|
|
55
|
+
|
|
56
|
+
# This is a table with orig subnet as the key, and subnet as the value.
|
|
57
|
+
const ignore_guessers: table[subnet] of subnet &redef;
|
|
58
|
+
|
|
59
|
+
# If true, we tell the event engine to not look at further data
|
|
60
|
+
# packets after the initial SSH handshake. Helps with performance
|
|
61
|
+
# (especially with large file transfers) but precludes some
|
|
62
|
+
# kinds of analyses (e.g., tracking connection size).
|
|
63
|
+
const skip_processing_after_detection = F &redef;
|
|
64
|
+
|
|
65
|
+
# Keeps count of how many rejections a host has had
|
|
66
|
+
global password_rejections: table[addr] of TrackCount
|
|
67
|
+
&write_expire=guessing_timeout
|
|
68
|
+
&synchronized;
|
|
69
|
+
|
|
70
|
+
# Keeps track of hosts identified as guessing passwords
|
|
71
|
+
# TODO: guessing_timeout doesn't work correctly here. If a user redefs
|
|
72
|
+
# the variable, it won't take effect.
|
|
73
|
+
global password_guessers: set[addr] &read_expire=guessing_timeout+1hr &synchronized;
|
|
74
|
+
|
|
75
|
+
global log_ssh: event(rec: Info);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
# Configure DPD and the packet filter
|
|
79
|
+
redef capture_filters += { ["ssh"] = "tcp port 22" };
|
|
80
|
+
redef dpd_config += { [ANALYZER_SSH] = [$ports = set(22/tcp)] };
|
|
81
|
+
|
|
82
|
+
redef record connection += {
|
|
83
|
+
ssh: Info &optional;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
event bro_init()
|
|
87
|
+
{
|
|
88
|
+
Log::create_stream(SSH, [$columns=Info, $ev=log_ssh]);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function set_session(c: connection)
|
|
92
|
+
{
|
|
93
|
+
if ( ! c?$ssh )
|
|
94
|
+
{
|
|
95
|
+
local info: Info;
|
|
96
|
+
info$ts=network_time();
|
|
97
|
+
info$uid=c$uid;
|
|
98
|
+
info$id=c$id;
|
|
99
|
+
c$ssh = info;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function check_ssh_connection(c: connection, done: bool)
|
|
104
|
+
{
|
|
105
|
+
# If done watching this connection, just return.
|
|
106
|
+
if ( c$ssh$done )
|
|
107
|
+
return;
|
|
108
|
+
|
|
109
|
+
# If this is still a live connection and the byte count has not
|
|
110
|
+
# crossed the threshold, just return and let the resheduled check happen later.
|
|
111
|
+
if ( !done && c$resp$size < authentication_data_size )
|
|
112
|
+
return;
|
|
113
|
+
|
|
114
|
+
# Make sure the server has sent back more than 50 bytes to filter out
|
|
115
|
+
# hosts that are just port scanning. Nothing is ever logged if the server
|
|
116
|
+
# doesn't send back at least 50 bytes.
|
|
117
|
+
if ( c$resp$size < 50 )
|
|
118
|
+
return;
|
|
119
|
+
|
|
120
|
+
local status = "failure";
|
|
121
|
+
local direction = Site::is_local_addr(c$id$orig_h) ? "to" : "from";
|
|
122
|
+
local location: geo_location;
|
|
123
|
+
location = (direction == "to") ? lookup_location(c$id$resp_h) : lookup_location(c$id$orig_h);
|
|
124
|
+
|
|
125
|
+
if ( done && c$resp$size < authentication_data_size )
|
|
126
|
+
{
|
|
127
|
+
# presumed failure
|
|
128
|
+
if ( c$id$orig_h !in password_rejections )
|
|
129
|
+
password_rejections[c$id$orig_h] = new_track_count();
|
|
130
|
+
|
|
131
|
+
# Track the number of rejections
|
|
132
|
+
if ( !(c$id$orig_h in ignore_guessers &&
|
|
133
|
+
c$id$resp_h in ignore_guessers[c$id$orig_h]) )
|
|
134
|
+
++password_rejections[c$id$orig_h]$n;
|
|
135
|
+
|
|
136
|
+
if ( default_check_threshold(password_rejections[c$id$orig_h]) )
|
|
137
|
+
{
|
|
138
|
+
add password_guessers[c$id$orig_h];
|
|
139
|
+
NOTICE([$note=Password_Guessing,
|
|
140
|
+
$conn=c,
|
|
141
|
+
$msg=fmt("SSH password guessing by %s", c$id$orig_h),
|
|
142
|
+
$sub=fmt("%d failed logins", password_rejections[c$id$orig_h]$n),
|
|
143
|
+
$n=password_rejections[c$id$orig_h]$n]);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
# TODO: This is to work around a quasi-bug in Bro which occasionally
|
|
147
|
+
# causes the byte count to be oversized.
|
|
148
|
+
# Watch for Gregors work that adds an actual counter of bytes transferred.
|
|
149
|
+
else if ( c$resp$size < 20000000 )
|
|
150
|
+
{
|
|
151
|
+
# presumed successful login
|
|
152
|
+
status = "success";
|
|
153
|
+
c$ssh$done = T;
|
|
154
|
+
|
|
155
|
+
if ( c$id$orig_h in password_rejections &&
|
|
156
|
+
password_rejections[c$id$orig_h]$n > password_guesses_limit &&
|
|
157
|
+
c$id$orig_h !in password_guessers )
|
|
158
|
+
{
|
|
159
|
+
add password_guessers[c$id$orig_h];
|
|
160
|
+
NOTICE([$note=Login_By_Password_Guesser,
|
|
161
|
+
$conn=c,
|
|
162
|
+
$n=password_rejections[c$id$orig_h]$n,
|
|
163
|
+
$msg=fmt("Successful SSH login by password guesser %s", c$id$orig_h),
|
|
164
|
+
$sub=fmt("%d failed logins", password_rejections[c$id$orig_h]$n)]);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
local message = fmt("SSH login %s %s \"%s\" \"%s\" %f %f %s (triggered with %d bytes)",
|
|
168
|
+
direction, location$country_code, location$region, location$city,
|
|
169
|
+
location$latitude, location$longitude,
|
|
170
|
+
id_string(c$id), c$resp$size);
|
|
171
|
+
NOTICE([$note=Login,
|
|
172
|
+
$conn=c,
|
|
173
|
+
$msg=message,
|
|
174
|
+
$sub=location$country_code]);
|
|
175
|
+
|
|
176
|
+
# Check to see if this login came from an interesting hostname
|
|
177
|
+
when ( local hostname = lookup_addr(c$id$orig_h) )
|
|
178
|
+
{
|
|
179
|
+
if ( interesting_hostnames in hostname )
|
|
180
|
+
{
|
|
181
|
+
NOTICE([$note=Login_From_Interesting_Hostname,
|
|
182
|
+
$conn=c,
|
|
183
|
+
$msg=fmt("Strange login from %s", hostname),
|
|
184
|
+
$sub=hostname]);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if ( location$country_code in watched_countries )
|
|
189
|
+
{
|
|
190
|
+
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
}
|
|
194
|
+
else if ( c$resp$size >= 200000000 )
|
|
195
|
+
{
|
|
196
|
+
NOTICE([$note=Bytecount_Inconsistency,
|
|
197
|
+
$conn=c,
|
|
198
|
+
$msg="During byte counting in SSH analysis, an overly large value was seen.",
|
|
199
|
+
$sub=fmt("%d",c$resp$size)]);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
c$ssh$remote_location = location;
|
|
203
|
+
c$ssh$status = status;
|
|
204
|
+
c$ssh$direction = direction;
|
|
205
|
+
c$ssh$resp_size = c$resp$size;
|
|
206
|
+
|
|
207
|
+
Log::write(SSH, c$ssh);
|
|
208
|
+
|
|
209
|
+
# Set the "done" flag to prevent the watching event from rescheduling
|
|
210
|
+
# after detection is done.
|
|
211
|
+
c$ssh$done;
|
|
212
|
+
|
|
213
|
+
# Stop watching this connection, we don't care about it anymore.
|
|
214
|
+
if ( skip_processing_after_detection )
|
|
215
|
+
{
|
|
216
|
+
skip_further_processing(c$id);
|
|
217
|
+
set_record_packets(c$id, F);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
event connection_state_remove(c: connection) &priority=-5
|
|
222
|
+
{
|
|
223
|
+
if ( c?$ssh )
|
|
224
|
+
check_ssh_connection(c, T);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
event ssh_watcher(c: connection)
|
|
228
|
+
{
|
|
229
|
+
local id = c$id;
|
|
230
|
+
# don't go any further if this connection is gone already!
|
|
231
|
+
if ( !connection_exists(id) )
|
|
232
|
+
return;
|
|
233
|
+
|
|
234
|
+
check_ssh_connection(c, F);
|
|
235
|
+
if ( ! c$ssh$done )
|
|
236
|
+
schedule +15secs { ssh_watcher(c) };
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
event ssh_server_version(c: connection, version: string) &priority=5
|
|
240
|
+
{
|
|
241
|
+
set_session(c);
|
|
242
|
+
c$ssh$server = version;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
event ssh_client_version(c: connection, version: string) &priority=5
|
|
246
|
+
{
|
|
247
|
+
set_session(c);
|
|
248
|
+
c$ssh$client = version;
|
|
249
|
+
schedule +15secs { ssh_watcher(c) };
|
|
250
|
+
}
|
|
@@ -153,6 +153,29 @@ namespace Diva.Core {
|
|
|
153
153
|
public OpenerTask (string fileName)
|
|
154
154
|
{
|
|
155
155
|
this.fileName = fileName;
|
|
156
|
+
var verbatimString = @"c:\test\";
|
|
157
|
+
|
|
158
|
+
var verbatimStringWithNewline = @"test \\ \n \t \r
|
|
159
|
+
a
|
|
160
|
+
b
|
|
161
|
+
c";
|
|
162
|
+
var verbatimStringWithEscapedQuotes = @"He said
|
|
163
|
+
""she says \"" is not an escaped character in verbatimstrings""
|
|
164
|
+
";
|
|
165
|
+
|
|
166
|
+
int[] numbers = { 5,6,4,2,4,6,8,9,7,0 };
|
|
167
|
+
var linqExample = from n in numbers
|
|
168
|
+
where n > 5
|
|
169
|
+
select n;
|
|
170
|
+
|
|
171
|
+
var anotherlinqExample = from n in numbers
|
|
172
|
+
orderby n descending
|
|
173
|
+
select n;
|
|
174
|
+
|
|
175
|
+
int[] someMoreNumbers = { 8,2,17,34,8,9,9,5,3,4,2,1,5 };
|
|
176
|
+
var moreLinq = from n in numbers
|
|
177
|
+
join mn in moreNumbers on n equals mn + 2
|
|
178
|
+
select new {n, mn};
|
|
156
179
|
}
|
|
157
180
|
|
|
158
181
|
public override void Reset ()
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Greeter example from
|
|
2
|
+
// <http://www.dartlang.org/docs/getting-started/interface.html>
|
|
3
|
+
class Greeter implements Comparable {
|
|
4
|
+
String prefix = 'Hello,';
|
|
5
|
+
Greeter() {}
|
|
6
|
+
Greeter.withPrefix(this.prefix);
|
|
7
|
+
greet(String name) => print('$prefix $name');
|
|
8
|
+
|
|
9
|
+
int compareTo(Greeter other) => prefix.compareTo(other.prefix);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
void main() {
|
|
13
|
+
Greeter greeter = new Greeter();
|
|
14
|
+
Greeter greeter2 = new Greeter.withPrefix('Hi,');
|
|
15
|
+
|
|
16
|
+
num result = greeter2.compareTo(greeter);
|
|
17
|
+
if (result == 0) {
|
|
18
|
+
greeter2.greet('you are the same.');
|
|
19
|
+
} else {
|
|
20
|
+
greeter2.greet('you are different.');
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/*##############################################################################
|
|
2
|
+
|
|
3
|
+
Copyright (C) 2011 HPCC Systems.
|
|
4
|
+
|
|
5
|
+
All rights reserved. This program is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU Affero General Public License as
|
|
7
|
+
published by the Free Software Foundation, either version 3 of the
|
|
8
|
+
License, or (at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU Affero General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
+
############################################################################## */
|
|
18
|
+
|
|
19
|
+
#option ('slidingJoins', true);
|
|
20
|
+
|
|
21
|
+
namesRecord :=
|
|
22
|
+
RECORD
|
|
23
|
+
string20 surname;
|
|
24
|
+
string10 forename;
|
|
25
|
+
integer2 age;
|
|
26
|
+
integer2 dadAge;
|
|
27
|
+
integer2 mumAge;
|
|
28
|
+
END;
|
|
29
|
+
|
|
30
|
+
namesRecord2 :=
|
|
31
|
+
record
|
|
32
|
+
string10 extra;
|
|
33
|
+
namesRecord;
|
|
34
|
+
end;
|
|
35
|
+
|
|
36
|
+
namesTable := dataset('x',namesRecord,FLAT);
|
|
37
|
+
namesTable2 := dataset('y',namesRecord2,FLAT);
|
|
38
|
+
|
|
39
|
+
integer2 aveAgeL(namesRecord l) := (l.dadAge+l.mumAge)/2;
|
|
40
|
+
integer2 aveAgeR(namesRecord2 r) := (r.dadAge+r.mumAge)/2;
|
|
41
|
+
|
|
42
|
+
// Standard join on a function of left and right
|
|
43
|
+
output(join(namesTable, namesTable2, aveAgeL(left) = aveAgeR(right)));
|
|
44
|
+
|
|
45
|
+
//Several simple examples of sliding join syntax
|
|
46
|
+
output(join(namesTable, namesTable2, left.age >= right.age - 10 and left.age <= right.age +10));
|
|
47
|
+
output(join(namesTable, namesTable2, left.age between right.age - 10 and right.age +10));
|
|
48
|
+
output(join(namesTable, namesTable2, left.age between right.age + 10 and right.age +30));
|
|
49
|
+
output(join(namesTable, namesTable2, left.age between (right.age + 20) - 10 and (right.age +20) + 10));
|
|
50
|
+
output(join(namesTable, namesTable2, aveAgeL(left) between aveAgeR(right)+10 and aveAgeR(right)+40));
|
|
51
|
+
|
|
52
|
+
//Same, but on strings. Also includes age to ensure sort is done by non-sliding before sliding.
|
|
53
|
+
output(join(namesTable, namesTable2, left.surname between right.surname[1..10]+'AAAAAAAAAA' and right.surname[1..10]+'ZZZZZZZZZZ' and left.age=right.age));
|
|
54
|
+
output(join(namesTable, namesTable2, left.surname between right.surname[1..10]+'AAAAAAAAAA' and right.surname[1..10]+'ZZZZZZZZZZ' and left.age=right.age,all));
|
|
55
|
+
|
|
56
|
+
//This should not generate a self join
|
|
57
|
+
output(join(namesTable, namesTable, left.age between right.age - 10 and right.age +10));
|
|
58
|
+
|
|
@@ -0,0 +1,818 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright (c) 2008, Brian Frank and Andy Frank
|
|
3
|
+
// Licensed under the Academic Free License version 3.0
|
|
4
|
+
//
|
|
5
|
+
// History:
|
|
6
|
+
// 17 Nov 08 Brian Frank Creation
|
|
7
|
+
//
|
|
8
|
+
|
|
9
|
+
using compiler
|
|
10
|
+
|
|
11
|
+
**
|
|
12
|
+
** JavaBridge is the compiler plugin for bringing Java
|
|
13
|
+
** classes into the Fantom type system.
|
|
14
|
+
**
|
|
15
|
+
class JavaBridge : CBridge
|
|
16
|
+
{
|
|
17
|
+
|
|
18
|
+
//////////////////////////////////////////////////////////////////////////
|
|
19
|
+
// Constructor
|
|
20
|
+
//////////////////////////////////////////////////////////////////////////
|
|
21
|
+
|
|
22
|
+
**
|
|
23
|
+
** Construct a JavaBridge for current environment
|
|
24
|
+
**
|
|
25
|
+
new make(Compiler c, ClassPath cp := ClassPath.makeForCurrent)
|
|
26
|
+
: super(c)
|
|
27
|
+
{
|
|
28
|
+
this.cp = cp
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
//////////////////////////////////////////////////////////////////////////
|
|
32
|
+
// Namespace
|
|
33
|
+
//////////////////////////////////////////////////////////////////////////
|
|
34
|
+
|
|
35
|
+
**
|
|
36
|
+
** Map a FFI "podName" to a Java package.
|
|
37
|
+
**
|
|
38
|
+
override CPod resolvePod(Str name, Loc? loc)
|
|
39
|
+
{
|
|
40
|
+
// the empty package is used to represent primitives
|
|
41
|
+
if (name == "") return primitives
|
|
42
|
+
|
|
43
|
+
// look for package name in classpatch
|
|
44
|
+
classes := cp.classes[name]
|
|
45
|
+
if (classes == null)
|
|
46
|
+
throw CompilerErr("Java package '$name' not found", loc)
|
|
47
|
+
|
|
48
|
+
// map package to JavaPod
|
|
49
|
+
return JavaPod(this, name, classes)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
**
|
|
53
|
+
** Map class meta-data and Java members to Fantom slots
|
|
54
|
+
** for the specified JavaType.
|
|
55
|
+
**
|
|
56
|
+
virtual Void loadType(JavaType type, Str:CSlot slots)
|
|
57
|
+
{
|
|
58
|
+
JavaReflect.loadType(type, slots)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
//////////////////////////////////////////////////////////////////////////
|
|
62
|
+
// Call Resolution
|
|
63
|
+
//////////////////////////////////////////////////////////////////////////
|
|
64
|
+
|
|
65
|
+
**
|
|
66
|
+
** Resolve a construction call to a Java constructor.
|
|
67
|
+
**
|
|
68
|
+
override Expr resolveConstruction(CallExpr call)
|
|
69
|
+
{
|
|
70
|
+
// if the last argument is an it-block, then we know
|
|
71
|
+
// right away that we will not be passing it thru to Java,
|
|
72
|
+
// so strip it off to be appended as call to Obj.with
|
|
73
|
+
itBlock := call.args.last as ClosureExpr
|
|
74
|
+
if (itBlock != null && itBlock.isItBlock)
|
|
75
|
+
call.args.removeAt(-1)
|
|
76
|
+
else
|
|
77
|
+
itBlock = null
|
|
78
|
+
|
|
79
|
+
// if this is an interop array like IntArray/int[] use make
|
|
80
|
+
// factory otherwise look for Java constructor called <init>
|
|
81
|
+
JavaType base := call.target.ctype
|
|
82
|
+
if (base.isInteropArray)
|
|
83
|
+
call.method = base.method("make")
|
|
84
|
+
else
|
|
85
|
+
call.method = base.method("<init>")
|
|
86
|
+
|
|
87
|
+
// call resolution to deal with overloading
|
|
88
|
+
call = resolveCall(call)
|
|
89
|
+
|
|
90
|
+
// we need to create an implicit target for the Java runtime
|
|
91
|
+
// to perform the new opcode to ensure it is on the stack
|
|
92
|
+
// before the args (we don't do this for interop Array classes)
|
|
93
|
+
if (!base.isInteropArray)
|
|
94
|
+
{
|
|
95
|
+
loc := call.loc
|
|
96
|
+
call.target = CallExpr.makeWithMethod(loc, null, base.newMethod) { synthetic=true }
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// if we stripped an it-block argument,
|
|
100
|
+
// add it as trailing call to Obj.with
|
|
101
|
+
if (itBlock != null) return itBlock.toWith(call)
|
|
102
|
+
return call
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
**
|
|
106
|
+
** Resolve a construction chain call where a Fantom constructor
|
|
107
|
+
** calls the super-class constructor. Type check the arguments
|
|
108
|
+
** and insert any conversions needed.
|
|
109
|
+
**
|
|
110
|
+
override Expr resolveConstructorChain(CallExpr call)
|
|
111
|
+
{
|
|
112
|
+
// we don't allow chaining to a this ctor for Java FFI
|
|
113
|
+
if (call.target.id !== ExprId.superExpr)
|
|
114
|
+
throw err("Must use super constructor call in Java FFI", call.loc)
|
|
115
|
+
|
|
116
|
+
// route to a superclass constructor
|
|
117
|
+
JavaType base := call.target.ctype.deref
|
|
118
|
+
call.method = base.method("<init>")
|
|
119
|
+
|
|
120
|
+
// call resolution to deal with overloading
|
|
121
|
+
return resolveCall(call)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
**
|
|
125
|
+
** Given a dot operator slot access on the given foreign
|
|
126
|
+
** base type, determine the appopriate slot to use based on
|
|
127
|
+
** whether parens were used
|
|
128
|
+
** base.name => noParens = true
|
|
129
|
+
** base.name() => noParens = false
|
|
130
|
+
**
|
|
131
|
+
** In Java a given name could be bound to both a field and
|
|
132
|
+
** a method. In this case we only resolve the field if
|
|
133
|
+
** no parens are used. We also handle the special case of
|
|
134
|
+
** Java annotations here because their element methods are
|
|
135
|
+
** also mapped as Fantom fields (instance based mixin field).
|
|
136
|
+
**
|
|
137
|
+
override CSlot? resolveSlotAccess(CType base, Str name, Bool noParens)
|
|
138
|
+
{
|
|
139
|
+
// first try to resolve as a field
|
|
140
|
+
field := base.field(name)
|
|
141
|
+
if (field != null)
|
|
142
|
+
{
|
|
143
|
+
// if no () we used and this isn't an annotation field
|
|
144
|
+
if (noParens && (field.isStatic || !base.isMixin))
|
|
145
|
+
return field
|
|
146
|
+
|
|
147
|
+
// if we did find a field, then make sure we use that
|
|
148
|
+
// field's parent type to resolve a method (becuase the
|
|
149
|
+
// base type might be a sub-class of a Java type in which
|
|
150
|
+
// case it is unware of field/method overloads)
|
|
151
|
+
return field.parent.method(name)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// lookup method
|
|
155
|
+
return base.method(name)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
**
|
|
159
|
+
** Resolve a method call: try to find the best match
|
|
160
|
+
** and apply any coercions needed.
|
|
161
|
+
**
|
|
162
|
+
override CallExpr resolveCall(CallExpr call)
|
|
163
|
+
{
|
|
164
|
+
// try to match against all the overloaded methods
|
|
165
|
+
matches := CallMatch[,]
|
|
166
|
+
CMethod? m := call.method
|
|
167
|
+
while (m != null)
|
|
168
|
+
{
|
|
169
|
+
match := matchCall(call, m)
|
|
170
|
+
if (match != null) matches.add(match)
|
|
171
|
+
m = m is JavaMethod ? ((JavaMethod)m).next : null
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// if we have exactly one match use then use that one
|
|
175
|
+
if (matches.size == 1) return matches[0].apply(call)
|
|
176
|
+
|
|
177
|
+
// if we have multiple matches; resolve to
|
|
178
|
+
// most specific match according to JLS rules
|
|
179
|
+
// TODO: this does not correct resolve when using Fantom implicit casting
|
|
180
|
+
if (matches.size > 1)
|
|
181
|
+
{
|
|
182
|
+
best := resolveMostSpecific(matches)
|
|
183
|
+
if (best != null) return best.apply(call)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// zero or multiple ambiguous matches is a compiler error
|
|
187
|
+
s := StrBuf()
|
|
188
|
+
s.add(matches.isEmpty ? "Invalid args " : "Ambiguous call ")
|
|
189
|
+
s.add(call.name).add("(")
|
|
190
|
+
s.add(call.args.join(", ") |Expr arg->Str| { return arg.toTypeStr })
|
|
191
|
+
s.add(")")
|
|
192
|
+
throw err(s.toStr, call.loc)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
**
|
|
196
|
+
** Check if the call matches the specified overload method.
|
|
197
|
+
** If so return method and coerced args otherwise return null.
|
|
198
|
+
**
|
|
199
|
+
internal CallMatch? matchCall(CallExpr call, CMethod m)
|
|
200
|
+
{
|
|
201
|
+
// first check if have matching numbers of args and params
|
|
202
|
+
args := call.args
|
|
203
|
+
if (m.params.size < args.size) return null
|
|
204
|
+
|
|
205
|
+
// check if each argument is ok or can be coerced
|
|
206
|
+
isErr := false
|
|
207
|
+
newArgs := args.dup
|
|
208
|
+
m.params.each |CParam p, Int i|
|
|
209
|
+
{
|
|
210
|
+
if (i >= args.size)
|
|
211
|
+
{
|
|
212
|
+
// param has a default value, then that is ok
|
|
213
|
+
if (!p.hasDefault) isErr = true
|
|
214
|
+
}
|
|
215
|
+
else
|
|
216
|
+
{
|
|
217
|
+
// ensure arg fits parameter type (or auto-cast)
|
|
218
|
+
newArgs[i] = coerce(args[i], p.paramType) |->| { isErr = true }
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (isErr) return null
|
|
222
|
+
return CallMatch { it.method = m; it.args = newArgs }
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
**
|
|
226
|
+
** Given a list of overloaed methods find the most specific method
|
|
227
|
+
** according to Java Language Specification 15.11.2.2. The "informal
|
|
228
|
+
** intuition" rule is that a method is more specific than another
|
|
229
|
+
** if the first could be could be passed onto the second one.
|
|
230
|
+
**
|
|
231
|
+
internal static CallMatch? resolveMostSpecific(CallMatch[] matches)
|
|
232
|
+
{
|
|
233
|
+
CallMatch? best := matches[0]
|
|
234
|
+
for (i:=1; i<matches.size; ++i)
|
|
235
|
+
{
|
|
236
|
+
x := matches[i]
|
|
237
|
+
if (isMoreSpecific(best, x)) { continue }
|
|
238
|
+
if (isMoreSpecific(x, best)) { best = x; continue }
|
|
239
|
+
return null
|
|
240
|
+
}
|
|
241
|
+
return best
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
**
|
|
245
|
+
** Is 'a' more specific than 'b' such that 'a' could be used
|
|
246
|
+
** passed to 'b' without a compile time error.
|
|
247
|
+
**
|
|
248
|
+
internal static Bool isMoreSpecific(CallMatch a, CallMatch b)
|
|
249
|
+
{
|
|
250
|
+
return a.method.params.all |CParam ap, Int i->Bool|
|
|
251
|
+
{
|
|
252
|
+
bp := b.method.params[i]
|
|
253
|
+
return ap.paramType.fits(bp.paramType)
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
//////////////////////////////////////////////////////////////////////////
|
|
258
|
+
// Overrides
|
|
259
|
+
//////////////////////////////////////////////////////////////////////////
|
|
260
|
+
|
|
261
|
+
**
|
|
262
|
+
** Called during Inherit step when a Fantom slot overrides a FFI slot.
|
|
263
|
+
** Log and throw compiler error if there is a problem.
|
|
264
|
+
**
|
|
265
|
+
override Void checkOverride(TypeDef t, CSlot base, SlotDef def)
|
|
266
|
+
{
|
|
267
|
+
// we don't allow Fantom to override Java methods with multiple
|
|
268
|
+
// overloaded versions since the Fantom type system can't actually
|
|
269
|
+
// override all the overloaded versions
|
|
270
|
+
jslot := base as JavaSlot
|
|
271
|
+
if (jslot?.next != null)
|
|
272
|
+
throw err("Cannot override Java overloaded method: '$jslot.name'", def.loc)
|
|
273
|
+
|
|
274
|
+
// route to method override checking
|
|
275
|
+
if (base is JavaMethod && def is MethodDef)
|
|
276
|
+
checkMethodOverride(t, base, def)
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
**
|
|
280
|
+
** Called on method/method overrides in the checkOverride callback.
|
|
281
|
+
**
|
|
282
|
+
private Void checkMethodOverride(TypeDef t, JavaMethod base, MethodDef def)
|
|
283
|
+
{
|
|
284
|
+
// bail early if we know things aren't going to work out
|
|
285
|
+
if (base.params.size != def.params.size) return
|
|
286
|
+
|
|
287
|
+
// if the return type is primitive or Java array and the
|
|
288
|
+
// Fantom declaration matches how it is inferred into the Fan
|
|
289
|
+
// type system, then just change the return type - the compiler
|
|
290
|
+
// will impliclty do all the return coercions
|
|
291
|
+
if (isOverrideInferredType(base.returnType, def.returnType))
|
|
292
|
+
{
|
|
293
|
+
def.ret = def.inheritedRet = base.returnType
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// if any of the parameters is a primitive or Java array
|
|
297
|
+
// and the Fantom declaration matches how it is inferred into
|
|
298
|
+
// the Fantom type type, then change the parameter type to
|
|
299
|
+
// the Java override type and make the Fantom type a local
|
|
300
|
+
// variable:
|
|
301
|
+
// Java: void foo(int a) { ... }
|
|
302
|
+
// Fantom: Void foo(Int a) { ... }
|
|
303
|
+
// Result: Void foo(int a_$J) { Int a := a_$J; ... }
|
|
304
|
+
//
|
|
305
|
+
base.params.eachr |CParam bp, Int i|
|
|
306
|
+
{
|
|
307
|
+
dp := def.paramDefs[i]
|
|
308
|
+
if (!isOverrideInferredType(bp.paramType, dp.paramType)) return
|
|
309
|
+
|
|
310
|
+
// add local variable: Int bar := bar_$J
|
|
311
|
+
local := LocalDefStmt(def.loc)
|
|
312
|
+
local.ctype = dp.paramType
|
|
313
|
+
local.name = dp.name
|
|
314
|
+
local.init = UnknownVarExpr(def.loc, null, dp.name + "_\$J")
|
|
315
|
+
def.code.stmts.insert(0, local)
|
|
316
|
+
|
|
317
|
+
// rename parameter Int bar -> int bar_$J
|
|
318
|
+
dp.name = dp.name + "_\$J"
|
|
319
|
+
dp.paramType = bp.paramType
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
**
|
|
324
|
+
** When overriding a Java method check if the base type is
|
|
325
|
+
** is a Java primitive or array and the override definition is
|
|
326
|
+
** matches how the Java type is inferred in the Fantom type system.
|
|
327
|
+
** If we have a match return true and we'll swizzle things in
|
|
328
|
+
** checkMethodOverride.
|
|
329
|
+
**
|
|
330
|
+
static private Bool isOverrideInferredType(CType base, CType def)
|
|
331
|
+
{
|
|
332
|
+
// check if base class slot is a JavaType
|
|
333
|
+
java := base.toNonNullable as JavaType
|
|
334
|
+
if (java != null)
|
|
335
|
+
{
|
|
336
|
+
// allow primitives is it matches the inferred type
|
|
337
|
+
if (java.isPrimitive) return java.inferredAs == def
|
|
338
|
+
|
|
339
|
+
// allow arrays if mapped as Foo[] -> Foo?[]?
|
|
340
|
+
if (java.isArray) return java.inferredAs == def.toNonNullable && def.isNullable
|
|
341
|
+
}
|
|
342
|
+
return false
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
//////////////////////////////////////////////////////////////////////////
|
|
346
|
+
// CheckErrors
|
|
347
|
+
//////////////////////////////////////////////////////////////////////////
|
|
348
|
+
|
|
349
|
+
**
|
|
350
|
+
** Called during CheckErrors step for a type which extends
|
|
351
|
+
** a FFI class or implements any FFI mixins.
|
|
352
|
+
**
|
|
353
|
+
override Void checkType(TypeDef def)
|
|
354
|
+
{
|
|
355
|
+
// can't subclass a primitive array like ByteArray/byte[]
|
|
356
|
+
if (def.base.deref is JavaType && def.base.deref->isInteropArray)
|
|
357
|
+
{
|
|
358
|
+
err("Cannot subclass from Java interop array: $def.base", def.loc)
|
|
359
|
+
return
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// we don't allow deep inheritance of Java classes because
|
|
363
|
+
// the Fantom constructor and Java constructor model don't match
|
|
364
|
+
// up past one level of inheritance
|
|
365
|
+
// NOTE: that that when we remove this restriction we need to
|
|
366
|
+
// test how field initialization works because instance$init
|
|
367
|
+
// is almost certain to break with the current emit design
|
|
368
|
+
javaBase := def.base
|
|
369
|
+
while (javaBase != null && !javaBase.isForeign) javaBase = javaBase.base
|
|
370
|
+
if (javaBase != null && javaBase !== def.base)
|
|
371
|
+
{
|
|
372
|
+
err("Cannot subclass Java class more than one level: $javaBase", def.loc)
|
|
373
|
+
return
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// ensure that when we map Fantom constructors to Java
|
|
377
|
+
// constructors that we don't have duplicate signatures
|
|
378
|
+
ctors := def.ctorDefs
|
|
379
|
+
ctors.each |MethodDef a, Int i|
|
|
380
|
+
{
|
|
381
|
+
ctors.each |MethodDef b, Int j|
|
|
382
|
+
{
|
|
383
|
+
if (i > j && areParamsSame(a, b))
|
|
384
|
+
err("Duplicate Java FFI constructor signatures: '$b.name' and '$a.name'", a.loc)
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
**
|
|
390
|
+
** Do the two methods have the exact same parameter types.
|
|
391
|
+
**
|
|
392
|
+
static Bool areParamsSame(CMethod a, CMethod b)
|
|
393
|
+
{
|
|
394
|
+
if (a.params.size != b.params.size) return false
|
|
395
|
+
for (i:=0; i<a.params.size; ++i)
|
|
396
|
+
{
|
|
397
|
+
if (a.params[i].paramType != b.params[i].paramType)
|
|
398
|
+
return false
|
|
399
|
+
}
|
|
400
|
+
return true
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
//////////////////////////////////////////////////////////////////////////
|
|
404
|
+
// Coercion
|
|
405
|
+
//////////////////////////////////////////////////////////////////////////
|
|
406
|
+
|
|
407
|
+
**
|
|
408
|
+
** Return if we can make the actual type fit the expected
|
|
409
|
+
** type, potentially using a coercion.
|
|
410
|
+
**
|
|
411
|
+
Bool fits(CType actual, CType expected)
|
|
412
|
+
{
|
|
413
|
+
// use dummy expression and route to coerce code
|
|
414
|
+
dummy := UnknownVarExpr(Loc("dummy"), null, "dummy") { ctype = actual }
|
|
415
|
+
fits := true
|
|
416
|
+
coerce(dummy, expected) |->| { fits=false }
|
|
417
|
+
return fits
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
**
|
|
421
|
+
** Coerce expression to expected type. If not a type match
|
|
422
|
+
** then run the onErr function.
|
|
423
|
+
**
|
|
424
|
+
override Expr coerce(Expr expr, CType expected, |->| onErr)
|
|
425
|
+
{
|
|
426
|
+
// handle easy case
|
|
427
|
+
actual := expr.ctype
|
|
428
|
+
expected = expected.deref
|
|
429
|
+
if (actual == expected) return expr
|
|
430
|
+
|
|
431
|
+
// handle null literal
|
|
432
|
+
if (expr.id === ExprId.nullLiteral && expected.isNullable)
|
|
433
|
+
return expr
|
|
434
|
+
|
|
435
|
+
// handle Fantom to Java primitives
|
|
436
|
+
if (expected.pod == primitives)
|
|
437
|
+
return coerceToPrimitive(expr, expected, onErr)
|
|
438
|
+
|
|
439
|
+
// handle Java primitives to Fan
|
|
440
|
+
if (actual.pod == primitives)
|
|
441
|
+
return coerceFromPrimitive(expr, expected, onErr)
|
|
442
|
+
|
|
443
|
+
// handle Java array to Fantom list
|
|
444
|
+
if (actual.name[0] == '[')
|
|
445
|
+
return coerceFromArray(expr, expected, onErr)
|
|
446
|
+
|
|
447
|
+
// handle Fantom list to Java array
|
|
448
|
+
if (expected.name[0] == '[')
|
|
449
|
+
return coerceToArray(expr, expected, onErr)
|
|
450
|
+
|
|
451
|
+
// handle sys::Func -> Java interface
|
|
452
|
+
if (actual is FuncType && expected.isMixin && expected.toNonNullable is JavaType)
|
|
453
|
+
return coerceFuncToInterface(expr, expected.toNonNullable, onErr)
|
|
454
|
+
|
|
455
|
+
// handle special classes and interfaces for built-in Fantom
|
|
456
|
+
// classes which actually map directly to Java built-in types
|
|
457
|
+
if (actual.isBool && boolTypes.contains(expected.toNonNullable.signature)) return box(expr)
|
|
458
|
+
if (actual.isInt && intTypes.contains(expected.toNonNullable.signature)) return box(expr)
|
|
459
|
+
if (actual.isFloat && floatTypes.contains(expected.toNonNullable.signature)) return box(expr)
|
|
460
|
+
if (actual.isDecimal && decimalTypes.contains(expected.toNonNullable.signature)) return expr
|
|
461
|
+
if (actual.isStr && strTypes.contains(expected.toNonNullable.signature)) return expr
|
|
462
|
+
|
|
463
|
+
// use normal Fantom coercion behavior
|
|
464
|
+
return super.coerce(expr, expected, onErr)
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
**
|
|
468
|
+
** Ensure value type is boxed.
|
|
469
|
+
**
|
|
470
|
+
private Expr box(Expr expr)
|
|
471
|
+
{
|
|
472
|
+
if (expr.ctype.isVal)
|
|
473
|
+
return TypeCheckExpr.coerce(expr, expr.ctype.toNullable)
|
|
474
|
+
else
|
|
475
|
+
return expr
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
**
|
|
479
|
+
** Coerce a fan expression to a Java primitive (other
|
|
480
|
+
** than the ones we support natively)
|
|
481
|
+
**
|
|
482
|
+
Expr coerceToPrimitive(Expr expr, JavaType expected, |->| onErr)
|
|
483
|
+
{
|
|
484
|
+
actual := expr.ctype
|
|
485
|
+
|
|
486
|
+
// sys::Int (long) -> int, short, byte
|
|
487
|
+
if (actual.isInt && expected.isPrimitiveIntLike)
|
|
488
|
+
return TypeCheckExpr.coerce(expr, expected)
|
|
489
|
+
|
|
490
|
+
// sys::Float (double) -> float
|
|
491
|
+
if (actual.isFloat && expected.isPrimitiveFloat)
|
|
492
|
+
return TypeCheckExpr.coerce(expr, expected)
|
|
493
|
+
|
|
494
|
+
// no coercion - type error
|
|
495
|
+
onErr()
|
|
496
|
+
return expr
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
**
|
|
500
|
+
** Coerce a Java primitive to a Fantom type.
|
|
501
|
+
**
|
|
502
|
+
Expr coerceFromPrimitive(Expr expr, CType expected, |->| onErr)
|
|
503
|
+
{
|
|
504
|
+
actual := (JavaType)expr.ctype
|
|
505
|
+
|
|
506
|
+
// int, short, byte -> sys::Int (long)
|
|
507
|
+
if (actual.isPrimitiveIntLike)
|
|
508
|
+
{
|
|
509
|
+
if (expected.isInt || expected.isObj)
|
|
510
|
+
return TypeCheckExpr.coerce(expr, expected)
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// float -> sys::Float (float)
|
|
514
|
+
if (actual.isPrimitiveFloat)
|
|
515
|
+
{
|
|
516
|
+
if (expected.isFloat || expected.isObj)
|
|
517
|
+
return TypeCheckExpr.coerce(expr, expected)
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// no coercion - type error
|
|
521
|
+
onErr()
|
|
522
|
+
return expr
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
**
|
|
526
|
+
** Coerce a Java array to a Fantom list.
|
|
527
|
+
**
|
|
528
|
+
Expr coerceFromArray(Expr expr, CType expected, |->| onErr)
|
|
529
|
+
{
|
|
530
|
+
actual := (JavaType)expr.ctype.toNonNullable
|
|
531
|
+
|
|
532
|
+
// if expected is array type
|
|
533
|
+
if (expected is JavaType && ((JavaType)expected).isArray)
|
|
534
|
+
if (actual.arrayOf.fits(((JavaType)expected).arrayOf)) return expr
|
|
535
|
+
|
|
536
|
+
// if expected is Obj
|
|
537
|
+
if (expected.isObj) return arrayToList(expr, actual.inferredArrayOf)
|
|
538
|
+
|
|
539
|
+
// if expected is list type
|
|
540
|
+
if (expected.toNonNullable is ListType)
|
|
541
|
+
{
|
|
542
|
+
expectedOf := ((ListType)expected.toNonNullable).v
|
|
543
|
+
if (actual.inferredArrayOf.fits(expectedOf)) return arrayToList(expr, expectedOf)
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// no coercion available
|
|
547
|
+
onErr()
|
|
548
|
+
return expr
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
**
|
|
552
|
+
** Generate List.make(of, expr) where expr is Object[]
|
|
553
|
+
**
|
|
554
|
+
private Expr arrayToList(Expr expr, CType of)
|
|
555
|
+
{
|
|
556
|
+
loc := expr.loc
|
|
557
|
+
ofExpr := LiteralExpr(loc, ExprId.typeLiteral, ns.typeType, of)
|
|
558
|
+
call := CallExpr.makeWithMethod(loc, null, listMakeFromArray, [ofExpr, expr])
|
|
559
|
+
call.synthetic = true
|
|
560
|
+
return call
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
**
|
|
564
|
+
** Coerce a Fantom list to Java array.
|
|
565
|
+
**
|
|
566
|
+
Expr coerceToArray(Expr expr, CType expected, |->| onErr)
|
|
567
|
+
{
|
|
568
|
+
loc := expr.loc
|
|
569
|
+
expectedOf := ((JavaType)expected.toNonNullable).inferredArrayOf
|
|
570
|
+
actual := expr.ctype
|
|
571
|
+
|
|
572
|
+
// if actual is list type
|
|
573
|
+
if (actual.toNonNullable is ListType)
|
|
574
|
+
{
|
|
575
|
+
actualOf := ((ListType)actual.toNonNullable).v
|
|
576
|
+
if (actualOf.fits(expectedOf))
|
|
577
|
+
{
|
|
578
|
+
// (Foo[])list.asArray(cls)
|
|
579
|
+
clsLiteral := CallExpr.makeWithMethod(loc, null, JavaType.classLiteral(this, expectedOf))
|
|
580
|
+
asArray := CallExpr.makeWithMethod(loc, expr, listAsArray, [clsLiteral])
|
|
581
|
+
return TypeCheckExpr.coerce(asArray, expected)
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// no coercion available
|
|
586
|
+
onErr()
|
|
587
|
+
return expr
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
**
|
|
591
|
+
** Attempt to coerce a parameterized sys::Func expr to a Java
|
|
592
|
+
** interface if the interface supports exactly one matching method.
|
|
593
|
+
**
|
|
594
|
+
Expr coerceFuncToInterface(Expr expr, JavaType expected, |->| onErr)
|
|
595
|
+
{
|
|
596
|
+
// check if we have exactly one abstract method in the expected type
|
|
597
|
+
loc := expr.loc
|
|
598
|
+
abstracts := expected.methods.findAll |CMethod m->Bool| { return m.isAbstract }
|
|
599
|
+
if (abstracts.size != 1) { onErr(); return expr }
|
|
600
|
+
method := abstracts.first
|
|
601
|
+
|
|
602
|
+
// check if we have a match
|
|
603
|
+
FuncType funcType := (FuncType)expr.ctype
|
|
604
|
+
if (!isFuncToInterfaceMatch(funcType, method)) { onErr(); return expr }
|
|
605
|
+
|
|
606
|
+
// check if we've already generated a wrapper for this combo
|
|
607
|
+
key := "${funcType.signature}+${method.qname}"
|
|
608
|
+
ctor := funcWrappers[key]
|
|
609
|
+
if (ctor == null)
|
|
610
|
+
{
|
|
611
|
+
ctor = generateFuncToInterfaceWrapper(expr.loc, funcType, expected, method)
|
|
612
|
+
funcWrappers[key] = ctor
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// replace expr with FuncWrapperX(expr)
|
|
616
|
+
call := CallExpr.makeWithMethod(loc, null, ctor, [expr])
|
|
617
|
+
call.synthetic = true
|
|
618
|
+
return call
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
**
|
|
622
|
+
** Return if the specified function type can be used to implement
|
|
623
|
+
** the specified interface method.
|
|
624
|
+
**
|
|
625
|
+
Bool isFuncToInterfaceMatch(FuncType funcType, CMethod method)
|
|
626
|
+
{
|
|
627
|
+
// sanity check to map to callX method - can't handle more than 8 args
|
|
628
|
+
if (method.params.size > 8) return false
|
|
629
|
+
|
|
630
|
+
// check if method is match for function; first check is that
|
|
631
|
+
// method must supply all the arguments required by the function
|
|
632
|
+
if (funcType.params.size > method.params.size) return false
|
|
633
|
+
|
|
634
|
+
// check that func return type fits method return
|
|
635
|
+
retOk := method.returnType.isVoid || fits(funcType.ret, method.returnType)
|
|
636
|
+
if (!retOk) return false
|
|
637
|
+
|
|
638
|
+
// check all the method parameters fit the function parameters
|
|
639
|
+
paramsOk := funcType.params.all |CType f, Int i->Bool| { return fits(f, method.params[i].paramType) }
|
|
640
|
+
if (!paramsOk) return false
|
|
641
|
+
|
|
642
|
+
return true
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
**
|
|
646
|
+
** Generate the wrapper which implements the specified expected interface
|
|
647
|
+
** and overrides the specified method which calls the function.
|
|
648
|
+
**
|
|
649
|
+
CMethod generateFuncToInterfaceWrapper(Loc loc, FuncType funcType, CType expected, CMethod method)
|
|
650
|
+
{
|
|
651
|
+
// Fantom: func typed as |Str|
|
|
652
|
+
// Java: interface Foo { void bar(String) }
|
|
653
|
+
// Result: FuncWrapperX(func)
|
|
654
|
+
//
|
|
655
|
+
// class FuncWrapperX : Foo
|
|
656
|
+
// {
|
|
657
|
+
// new make(Func f) { _func = f }
|
|
658
|
+
// override Void bar(Str a) { _func.call(a) }
|
|
659
|
+
// Func _func
|
|
660
|
+
// }
|
|
661
|
+
|
|
662
|
+
// generate FuncWrapper class
|
|
663
|
+
name := "FuncWrapper" + funcWrappers.size
|
|
664
|
+
cls := TypeDef(ns, loc, compiler.types[0].unit, name, FConst.Internal + FConst.Synthetic)
|
|
665
|
+
cls.base = ns.objType
|
|
666
|
+
cls.mixins = [expected]
|
|
667
|
+
addTypeDef(cls)
|
|
668
|
+
|
|
669
|
+
// generate FuncWrapper._func field
|
|
670
|
+
field := FieldDef(loc, cls)
|
|
671
|
+
((SlotDef)field).name = "_func"
|
|
672
|
+
((DefNode)field).flags = FConst.Private + FConst.Storage + FConst.Synthetic
|
|
673
|
+
field.fieldType = funcType
|
|
674
|
+
cls.addSlot(field)
|
|
675
|
+
|
|
676
|
+
// generate FuncWrapper.make constructor
|
|
677
|
+
ctor := MethodDef(loc, cls, "make", FConst.Internal + FConst.Ctor + FConst.Synthetic)
|
|
678
|
+
ctor.ret = ns.voidType
|
|
679
|
+
ctor.paramDefs = [ParamDef(loc, funcType, "f")]
|
|
680
|
+
ctor.code = Block.make(loc)
|
|
681
|
+
ctor.code.stmts.add(BinaryExpr.makeAssign(
|
|
682
|
+
FieldExpr(loc, ThisExpr(loc), field),
|
|
683
|
+
UnknownVarExpr(loc, null, "f")).toStmt)
|
|
684
|
+
ctor.code.stmts.add(ReturnStmt.make(loc))
|
|
685
|
+
cls.addSlot(ctor)
|
|
686
|
+
|
|
687
|
+
// generate FuncWrapper override of abstract method
|
|
688
|
+
over := MethodDef(loc, cls, method.name, FConst.Public + FConst.Override + FConst.Synthetic)
|
|
689
|
+
over.ret = method.returnType
|
|
690
|
+
over.paramDefs = ParamDef[,]
|
|
691
|
+
over.code = Block.make(loc)
|
|
692
|
+
callArity := "call"
|
|
693
|
+
call := CallExpr.makeWithMethod(loc, FieldExpr(loc, ThisExpr(loc), field), funcType.method(callArity))
|
|
694
|
+
method.params.each |CParam param, Int i|
|
|
695
|
+
{
|
|
696
|
+
paramName := "p$i"
|
|
697
|
+
over.params.add(ParamDef(loc, param.paramType, paramName))
|
|
698
|
+
if (i < funcType.params.size)
|
|
699
|
+
call.args.add(UnknownVarExpr(loc, null, paramName))
|
|
700
|
+
}
|
|
701
|
+
if (method.returnType.isVoid)
|
|
702
|
+
over.code.stmts.add(call.toStmt).add(ReturnStmt(loc))
|
|
703
|
+
else
|
|
704
|
+
over.code.stmts.add(ReturnStmt(loc, call))
|
|
705
|
+
cls.addSlot(over)
|
|
706
|
+
|
|
707
|
+
// return the ctor which we use for coercion
|
|
708
|
+
return ctor
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
//////////////////////////////////////////////////////////////////////////
|
|
712
|
+
// Reflection
|
|
713
|
+
//////////////////////////////////////////////////////////////////////////
|
|
714
|
+
|
|
715
|
+
**
|
|
716
|
+
** Get a CMethod representation for 'List.make(Type, Object[])'
|
|
717
|
+
**
|
|
718
|
+
once CMethod listMakeFromArray()
|
|
719
|
+
{
|
|
720
|
+
return JavaMethod(
|
|
721
|
+
this.ns.listType,
|
|
722
|
+
"make",
|
|
723
|
+
FConst.Public + FConst.Static,
|
|
724
|
+
this.ns.listType.toNullable,
|
|
725
|
+
[
|
|
726
|
+
JavaParam("of", this.ns.typeType),
|
|
727
|
+
JavaParam("array", objectArrayType)
|
|
728
|
+
])
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
**
|
|
732
|
+
** Get a CMethod representation for 'Object[] List.asArray()'
|
|
733
|
+
**
|
|
734
|
+
once CMethod listAsArray()
|
|
735
|
+
{
|
|
736
|
+
return JavaMethod(
|
|
737
|
+
this.ns.listType,
|
|
738
|
+
"asArray",
|
|
739
|
+
FConst.Public,
|
|
740
|
+
objectArrayType,
|
|
741
|
+
[JavaParam("cls", classType)])
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
**
|
|
745
|
+
** Get a CType representation for 'java.lang.Class'
|
|
746
|
+
**
|
|
747
|
+
once JavaType classType()
|
|
748
|
+
{
|
|
749
|
+
return ns.resolveType("[java]java.lang::Class")
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
**
|
|
753
|
+
** Get a CType representation for 'java.lang.Object[]'
|
|
754
|
+
**
|
|
755
|
+
once JavaType objectArrayType()
|
|
756
|
+
{
|
|
757
|
+
return ns.resolveType("[java]java.lang::[Object")
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
//////////////////////////////////////////////////////////////////////////
|
|
761
|
+
// Fields
|
|
762
|
+
//////////////////////////////////////////////////////////////////////////
|
|
763
|
+
|
|
764
|
+
const static Str[] boolTypes := Str[
|
|
765
|
+
"[java]java.io::Serializable",
|
|
766
|
+
"[java]java.lang::Comparable",
|
|
767
|
+
]
|
|
768
|
+
|
|
769
|
+
const static Str[] intTypes := Str[
|
|
770
|
+
"[java]java.lang::Number",
|
|
771
|
+
"[java]java.io::Serializable",
|
|
772
|
+
"[java]java.lang::Comparable",
|
|
773
|
+
]
|
|
774
|
+
|
|
775
|
+
const static Str[] floatTypes := Str[
|
|
776
|
+
"[java]java.lang::Number",
|
|
777
|
+
"[java]java.io::Serializable",
|
|
778
|
+
"[java]java.lang::Comparable",
|
|
779
|
+
]
|
|
780
|
+
|
|
781
|
+
const static Str[] decimalTypes := Str[
|
|
782
|
+
"[java]java.lang::Number",
|
|
783
|
+
"[java]java.io::Serializable",
|
|
784
|
+
"[java]java.lang::Comparable",
|
|
785
|
+
]
|
|
786
|
+
|
|
787
|
+
const static Str[] strTypes := Str[
|
|
788
|
+
"[java]java.io::Serializable",
|
|
789
|
+
"[java]java.lang::CharSequence",
|
|
790
|
+
"[java]java.lang::Comparable",
|
|
791
|
+
]
|
|
792
|
+
|
|
793
|
+
JavaPrimitives primitives := JavaPrimitives(this)
|
|
794
|
+
ClassPath cp
|
|
795
|
+
|
|
796
|
+
private Str:CMethod funcWrappers := Str:CMethod[:] // funcType+method:ctor
|
|
797
|
+
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
**************************************************************************
|
|
801
|
+
** CallMatch
|
|
802
|
+
**************************************************************************
|
|
803
|
+
|
|
804
|
+
internal class CallMatch
|
|
805
|
+
{
|
|
806
|
+
CallExpr apply(CallExpr call)
|
|
807
|
+
{
|
|
808
|
+
call.args = args
|
|
809
|
+
call.method = method
|
|
810
|
+
call.ctype = method.isCtor ? method.parent : method.returnType
|
|
811
|
+
return call
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
override Str toStr() { return method.signature }
|
|
815
|
+
|
|
816
|
+
CMethod? method // matched method
|
|
817
|
+
Expr[]? args // coerced arguments
|
|
818
|
+
}
|