pygments.rb 0.2.4 → 0.2.6
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
}
|