Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
michael@0 | 1 | |
michael@0 | 2 | |
michael@0 | 3 | (function () { |
michael@0 | 4 | var tokenise = function (str) { |
michael@0 | 5 | var tokens = [] |
michael@0 | 6 | , re = { |
michael@0 | 7 | "float": /^-?(([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][-+]?[0-9]+)?|[0-9]+[Ee][-+]?[0-9]+)/ |
michael@0 | 8 | , "integer": /^-?(0([Xx][0-9A-Fa-f]+|[0-7]*)|[1-9][0-9]*)/ |
michael@0 | 9 | , "identifier": /^[A-Z_a-z][0-9A-Z_a-z]*/ |
michael@0 | 10 | , "string": /^"[^"]*"/ |
michael@0 | 11 | , "whitespace": /^(?:[\t\n\r ]+|[\t\n\r ]*((\/\/.*|\/\*(.|\n|\r)*?\*\/)[\t\n\r ]*))+/ |
michael@0 | 12 | , "other": /^[^\t\n\r 0-9A-Z_a-z]/ |
michael@0 | 13 | } |
michael@0 | 14 | , types = [] |
michael@0 | 15 | ; |
michael@0 | 16 | for (var k in re) types.push(k); |
michael@0 | 17 | while (str.length > 0) { |
michael@0 | 18 | var matched = false; |
michael@0 | 19 | for (var i = 0, n = types.length; i < n; i++) { |
michael@0 | 20 | var type = types[i]; |
michael@0 | 21 | str = str.replace(re[type], function (tok) { |
michael@0 | 22 | tokens.push({ type: type, value: tok }); |
michael@0 | 23 | matched = true; |
michael@0 | 24 | return ""; |
michael@0 | 25 | }); |
michael@0 | 26 | if (matched) break; |
michael@0 | 27 | } |
michael@0 | 28 | if (matched) continue; |
michael@0 | 29 | throw new Error("Token stream not progressing"); |
michael@0 | 30 | } |
michael@0 | 31 | return tokens; |
michael@0 | 32 | }; |
michael@0 | 33 | |
michael@0 | 34 | var parse = function (tokens) { |
michael@0 | 35 | var line = 1; |
michael@0 | 36 | tokens = tokens.slice(); |
michael@0 | 37 | |
michael@0 | 38 | var FLOAT = "float" |
michael@0 | 39 | , INT = "integer" |
michael@0 | 40 | , ID = "identifier" |
michael@0 | 41 | , STR = "string" |
michael@0 | 42 | , OTHER = "other" |
michael@0 | 43 | ; |
michael@0 | 44 | |
michael@0 | 45 | var WebIDLParseError = function (str, line, input, tokens) { |
michael@0 | 46 | this.message = str; |
michael@0 | 47 | this.line = line; |
michael@0 | 48 | this.input = input; |
michael@0 | 49 | this.tokens = tokens; |
michael@0 | 50 | }; |
michael@0 | 51 | WebIDLParseError.prototype.toString = function () { |
michael@0 | 52 | return this.message + ", line " + this.line + " (tokens: '" + this.input + "')\n" + |
michael@0 | 53 | JSON.stringify(this.tokens, null, 4); |
michael@0 | 54 | }; |
michael@0 | 55 | |
michael@0 | 56 | var error = function (str) { |
michael@0 | 57 | var tok = "", numTokens = 0, maxTokens = 5; |
michael@0 | 58 | while (numTokens < maxTokens && tokens.length > numTokens) { |
michael@0 | 59 | tok += tokens[numTokens].value; |
michael@0 | 60 | numTokens++; |
michael@0 | 61 | } |
michael@0 | 62 | throw new WebIDLParseError(str, line, tok, tokens.slice(0, 5)); |
michael@0 | 63 | }; |
michael@0 | 64 | |
michael@0 | 65 | var last_token = null; |
michael@0 | 66 | |
michael@0 | 67 | var consume = function (type, value) { |
michael@0 | 68 | if (!tokens.length || tokens[0].type !== type) return; |
michael@0 | 69 | if (typeof value === "undefined" || tokens[0].value === value) { |
michael@0 | 70 | last_token = tokens.shift(); |
michael@0 | 71 | if (type === ID) last_token.value = last_token.value.replace(/^_/, ""); |
michael@0 | 72 | return last_token; |
michael@0 | 73 | } |
michael@0 | 74 | }; |
michael@0 | 75 | |
michael@0 | 76 | var ws = function () { |
michael@0 | 77 | if (!tokens.length) return; |
michael@0 | 78 | if (tokens[0].type === "whitespace") { |
michael@0 | 79 | var t = tokens.shift(); |
michael@0 | 80 | t.value.replace(/\n/g, function (m) { line++; return m; }); |
michael@0 | 81 | return t; |
michael@0 | 82 | } |
michael@0 | 83 | }; |
michael@0 | 84 | |
michael@0 | 85 | var all_ws = function () { |
michael@0 | 86 | var t = { type: "whitespace", value: "" }; |
michael@0 | 87 | while (true) { |
michael@0 | 88 | var w = ws(); |
michael@0 | 89 | if (!w) break; |
michael@0 | 90 | t.value += w.value; |
michael@0 | 91 | } |
michael@0 | 92 | if (t.value.length > 0) return t; |
michael@0 | 93 | }; |
michael@0 | 94 | |
michael@0 | 95 | var integer_type = function () { |
michael@0 | 96 | var ret = ""; |
michael@0 | 97 | all_ws(); |
michael@0 | 98 | if (consume(ID, "unsigned")) ret = "unsigned "; |
michael@0 | 99 | all_ws(); |
michael@0 | 100 | if (consume(ID, "short")) return ret + "short"; |
michael@0 | 101 | if (consume(ID, "long")) { |
michael@0 | 102 | ret += "long"; |
michael@0 | 103 | all_ws(); |
michael@0 | 104 | if (consume(ID, "long")) return ret + " long"; |
michael@0 | 105 | return ret; |
michael@0 | 106 | } |
michael@0 | 107 | if (ret) error("Failed to parse integer type"); |
michael@0 | 108 | }; |
michael@0 | 109 | |
michael@0 | 110 | var float_type = function () { |
michael@0 | 111 | var ret = ""; |
michael@0 | 112 | all_ws(); |
michael@0 | 113 | if (consume(ID, "unrestricted")) ret = "unrestricted "; |
michael@0 | 114 | all_ws(); |
michael@0 | 115 | if (consume(ID, "float")) return ret + "float"; |
michael@0 | 116 | if (consume(ID, "double")) return ret + "double"; |
michael@0 | 117 | if (ret) error("Failed to parse float type"); |
michael@0 | 118 | }; |
michael@0 | 119 | |
michael@0 | 120 | var primitive_type = function () { |
michael@0 | 121 | var num_type = integer_type() || float_type(); |
michael@0 | 122 | if (num_type) return num_type; |
michael@0 | 123 | all_ws(); |
michael@0 | 124 | if (consume(ID, "boolean")) return "boolean"; |
michael@0 | 125 | if (consume(ID, "byte")) return "byte"; |
michael@0 | 126 | if (consume(ID, "octet")) return "octet"; |
michael@0 | 127 | }; |
michael@0 | 128 | |
michael@0 | 129 | var const_value = function () { |
michael@0 | 130 | if (consume(ID, "true")) return { type: "boolean", value: true }; |
michael@0 | 131 | if (consume(ID, "false")) return { type: "boolean", value: false }; |
michael@0 | 132 | if (consume(ID, "null")) return { type: "null" }; |
michael@0 | 133 | if (consume(ID, "Infinity")) return { type: "Infinity", negative: false }; |
michael@0 | 134 | if (consume(ID, "NaN")) return { type: "NaN" }; |
michael@0 | 135 | var ret = consume(FLOAT) || consume(INT); |
michael@0 | 136 | if (ret) return { type: "number", value: 1 * ret.value }; |
michael@0 | 137 | var tok = consume(OTHER, "-"); |
michael@0 | 138 | if (tok) { |
michael@0 | 139 | if (consume(ID, "Infinity")) return { type: "Infinity", negative: true }; |
michael@0 | 140 | else tokens.unshift(tok); |
michael@0 | 141 | } |
michael@0 | 142 | }; |
michael@0 | 143 | |
michael@0 | 144 | var type_suffix = function (obj) { |
michael@0 | 145 | while (true) { |
michael@0 | 146 | all_ws(); |
michael@0 | 147 | if (consume(OTHER, "?")) { |
michael@0 | 148 | if (obj.nullable) error("Can't nullable more than once"); |
michael@0 | 149 | obj.nullable = true; |
michael@0 | 150 | } |
michael@0 | 151 | else if (consume(OTHER, "[")) { |
michael@0 | 152 | all_ws(); |
michael@0 | 153 | consume(OTHER, "]") || error("Unterminated array type"); |
michael@0 | 154 | if (!obj.array) obj.array = 1; |
michael@0 | 155 | else obj.array++; |
michael@0 | 156 | } |
michael@0 | 157 | else return; |
michael@0 | 158 | } |
michael@0 | 159 | }; |
michael@0 | 160 | |
michael@0 | 161 | var single_type = function () { |
michael@0 | 162 | var prim = primitive_type() |
michael@0 | 163 | , ret = { sequence: false, nullable: false, array: false, union: false } |
michael@0 | 164 | ; |
michael@0 | 165 | if (prim) { |
michael@0 | 166 | ret.idlType = prim; |
michael@0 | 167 | } |
michael@0 | 168 | else if (consume(ID, "sequence")) { |
michael@0 | 169 | all_ws(); |
michael@0 | 170 | if (!consume(OTHER, "<")) { |
michael@0 | 171 | ret.idlType = "sequence"; |
michael@0 | 172 | } |
michael@0 | 173 | else { |
michael@0 | 174 | ret.sequence = true; |
michael@0 | 175 | ret.idlType = type() || error("Error parsing sequence type"); |
michael@0 | 176 | all_ws(); |
michael@0 | 177 | if (!consume(OTHER, ">")) error("Unterminated sequence"); |
michael@0 | 178 | all_ws(); |
michael@0 | 179 | if (consume(OTHER, "?")) ret.nullable = true; |
michael@0 | 180 | return ret; |
michael@0 | 181 | } |
michael@0 | 182 | } |
michael@0 | 183 | else { |
michael@0 | 184 | var name = consume(ID); |
michael@0 | 185 | if (!name) return; |
michael@0 | 186 | ret.idlType = name.value; |
michael@0 | 187 | } |
michael@0 | 188 | type_suffix(ret); |
michael@0 | 189 | if (ret.nullable && ret.idlType === "any") error("Type any cannot be made nullable"); |
michael@0 | 190 | return ret; |
michael@0 | 191 | }; |
michael@0 | 192 | |
michael@0 | 193 | var union_type = function () { |
michael@0 | 194 | all_ws(); |
michael@0 | 195 | if (!consume(OTHER, "(")) return; |
michael@0 | 196 | var ret = { sequence: false, nullable: false, array: false, union: true, idlType: [] }; |
michael@0 | 197 | var fst = type() || error("Union type with no content"); |
michael@0 | 198 | ret.idlType.push(fst); |
michael@0 | 199 | while (true) { |
michael@0 | 200 | all_ws(); |
michael@0 | 201 | if (!consume(ID, "or")) break; |
michael@0 | 202 | var typ = type() || error("No type after 'or' in union type"); |
michael@0 | 203 | ret.idlType.push(typ); |
michael@0 | 204 | } |
michael@0 | 205 | if (!consume(OTHER, ")")) error("Unterminated union type"); |
michael@0 | 206 | type_suffix(ret); |
michael@0 | 207 | return ret; |
michael@0 | 208 | }; |
michael@0 | 209 | |
michael@0 | 210 | var type = function () { |
michael@0 | 211 | return single_type() || union_type(); |
michael@0 | 212 | }; |
michael@0 | 213 | |
michael@0 | 214 | var argument = function () { |
michael@0 | 215 | var ret = { optional: false, variadic: false }; |
michael@0 | 216 | ret.extAttrs = extended_attrs(); |
michael@0 | 217 | all_ws(); |
michael@0 | 218 | if (consume(ID, "optional")) { |
michael@0 | 219 | ret.optional = true; |
michael@0 | 220 | all_ws(); |
michael@0 | 221 | } |
michael@0 | 222 | ret.idlType = type(); |
michael@0 | 223 | if (!ret.idlType) return; |
michael@0 | 224 | if (!ret.optional) { |
michael@0 | 225 | all_ws(); |
michael@0 | 226 | if (tokens.length >= 3 && |
michael@0 | 227 | tokens[0].type === "other" && tokens[0].value === "." && |
michael@0 | 228 | tokens[1].type === "other" && tokens[1].value === "." && |
michael@0 | 229 | tokens[2].type === "other" && tokens[2].value === "." |
michael@0 | 230 | ) { |
michael@0 | 231 | tokens.shift(); |
michael@0 | 232 | tokens.shift(); |
michael@0 | 233 | tokens.shift(); |
michael@0 | 234 | ret.variadic = true; |
michael@0 | 235 | } |
michael@0 | 236 | } |
michael@0 | 237 | all_ws(); |
michael@0 | 238 | var name = consume(ID) || error("No name in argument"); |
michael@0 | 239 | ret.name = name.value; |
michael@0 | 240 | if (ret.optional) { |
michael@0 | 241 | all_ws(); |
michael@0 | 242 | ret["default"] = default_(); |
michael@0 | 243 | } |
michael@0 | 244 | return ret; |
michael@0 | 245 | }; |
michael@0 | 246 | |
michael@0 | 247 | var argument_list = function () { |
michael@0 | 248 | var arg = argument(), ret = []; |
michael@0 | 249 | if (!arg) return ret; |
michael@0 | 250 | ret.push(arg); |
michael@0 | 251 | while (true) { |
michael@0 | 252 | all_ws(); |
michael@0 | 253 | if (!consume(OTHER, ",")) return ret; |
michael@0 | 254 | all_ws(); |
michael@0 | 255 | var nxt = argument() || error("Trailing comma in arguments list"); |
michael@0 | 256 | ret.push(nxt); |
michael@0 | 257 | } |
michael@0 | 258 | }; |
michael@0 | 259 | |
michael@0 | 260 | var simple_extended_attr = function () { |
michael@0 | 261 | all_ws(); |
michael@0 | 262 | var name = consume(ID); |
michael@0 | 263 | if (!name) return; |
michael@0 | 264 | var ret = { |
michael@0 | 265 | name: name.value |
michael@0 | 266 | , "arguments": null |
michael@0 | 267 | }; |
michael@0 | 268 | all_ws(); |
michael@0 | 269 | var eq = consume(OTHER, "="); |
michael@0 | 270 | if (eq) { |
michael@0 | 271 | all_ws(); |
michael@0 | 272 | ret.rhs = consume(ID); |
michael@0 | 273 | if (!ret.rhs) return error("No right hand side to extended attribute assignment"); |
michael@0 | 274 | } |
michael@0 | 275 | all_ws(); |
michael@0 | 276 | if (consume(OTHER, "(")) { |
michael@0 | 277 | ret["arguments"] = argument_list(); |
michael@0 | 278 | all_ws(); |
michael@0 | 279 | consume(OTHER, ")") || error("Unclosed argument in extended attribute"); |
michael@0 | 280 | } |
michael@0 | 281 | return ret; |
michael@0 | 282 | }; |
michael@0 | 283 | |
michael@0 | 284 | // Note: we parse something simpler than the official syntax. It's all that ever |
michael@0 | 285 | // seems to be used |
michael@0 | 286 | var extended_attrs = function () { |
michael@0 | 287 | var eas = []; |
michael@0 | 288 | all_ws(); |
michael@0 | 289 | if (!consume(OTHER, "[")) return eas; |
michael@0 | 290 | eas[0] = simple_extended_attr() || error("Extended attribute with not content"); |
michael@0 | 291 | all_ws(); |
michael@0 | 292 | while (consume(OTHER, ",")) { |
michael@0 | 293 | all_ws(); |
michael@0 | 294 | eas.push(simple_extended_attr() || error("Trailing comma in extended attribute")); |
michael@0 | 295 | all_ws(); |
michael@0 | 296 | } |
michael@0 | 297 | consume(OTHER, "]") || error("No end of extended attribute"); |
michael@0 | 298 | return eas; |
michael@0 | 299 | }; |
michael@0 | 300 | |
michael@0 | 301 | var default_ = function () { |
michael@0 | 302 | all_ws(); |
michael@0 | 303 | if (consume(OTHER, "=")) { |
michael@0 | 304 | all_ws(); |
michael@0 | 305 | var def = const_value(); |
michael@0 | 306 | if (def) { |
michael@0 | 307 | return def; |
michael@0 | 308 | } |
michael@0 | 309 | else { |
michael@0 | 310 | var str = consume(STR) || error("No value for default"); |
michael@0 | 311 | str.value = str.value.replace(/^"/, "").replace(/"$/, ""); |
michael@0 | 312 | return str; |
michael@0 | 313 | } |
michael@0 | 314 | } |
michael@0 | 315 | }; |
michael@0 | 316 | |
michael@0 | 317 | var const_ = function () { |
michael@0 | 318 | all_ws(); |
michael@0 | 319 | if (!consume(ID, "const")) return; |
michael@0 | 320 | var ret = { type: "const", nullable: false }; |
michael@0 | 321 | all_ws(); |
michael@0 | 322 | var typ = primitive_type(); |
michael@0 | 323 | if (!typ) { |
michael@0 | 324 | typ = consume(ID) || error("No type for const"); |
michael@0 | 325 | typ = typ.value; |
michael@0 | 326 | } |
michael@0 | 327 | ret.idlType = typ; |
michael@0 | 328 | all_ws(); |
michael@0 | 329 | if (consume(OTHER, "?")) { |
michael@0 | 330 | ret.nullable = true; |
michael@0 | 331 | all_ws(); |
michael@0 | 332 | } |
michael@0 | 333 | var name = consume(ID) || error("No name for const"); |
michael@0 | 334 | ret.name = name.value; |
michael@0 | 335 | all_ws(); |
michael@0 | 336 | consume(OTHER, "=") || error("No value assignment for const"); |
michael@0 | 337 | all_ws(); |
michael@0 | 338 | var cnt = const_value(); |
michael@0 | 339 | if (cnt) ret.value = cnt; |
michael@0 | 340 | else error("No value for const"); |
michael@0 | 341 | all_ws(); |
michael@0 | 342 | consume(OTHER, ";") || error("Unterminated const"); |
michael@0 | 343 | return ret; |
michael@0 | 344 | }; |
michael@0 | 345 | |
michael@0 | 346 | var inheritance = function () { |
michael@0 | 347 | all_ws(); |
michael@0 | 348 | if (consume(OTHER, ":")) { |
michael@0 | 349 | all_ws(); |
michael@0 | 350 | var inh = consume(ID) || error ("No type in inheritance"); |
michael@0 | 351 | return inh.value; |
michael@0 | 352 | } |
michael@0 | 353 | }; |
michael@0 | 354 | |
michael@0 | 355 | var operation_rest = function (ret) { |
michael@0 | 356 | all_ws(); |
michael@0 | 357 | if (!ret) ret = {}; |
michael@0 | 358 | var name = consume(ID); |
michael@0 | 359 | ret.name = name ? name.value : null; |
michael@0 | 360 | all_ws(); |
michael@0 | 361 | consume(OTHER, "(") || error("Invalid operation"); |
michael@0 | 362 | ret["arguments"] = argument_list(); |
michael@0 | 363 | all_ws(); |
michael@0 | 364 | consume(OTHER, ")") || error("Unterminated operation"); |
michael@0 | 365 | all_ws(); |
michael@0 | 366 | consume(OTHER, ";") || error("Unterminated operation"); |
michael@0 | 367 | return ret; |
michael@0 | 368 | }; |
michael@0 | 369 | |
michael@0 | 370 | var callback = function () { |
michael@0 | 371 | all_ws(); |
michael@0 | 372 | var ret; |
michael@0 | 373 | if (!consume(ID, "callback")) return; |
michael@0 | 374 | all_ws(); |
michael@0 | 375 | var tok = consume(ID, "interface"); |
michael@0 | 376 | if (tok) { |
michael@0 | 377 | tokens.unshift(tok); |
michael@0 | 378 | ret = interface_(); |
michael@0 | 379 | ret.type = "callback interface"; |
michael@0 | 380 | return ret; |
michael@0 | 381 | } |
michael@0 | 382 | var name = consume(ID) || error("No name for callback"); |
michael@0 | 383 | ret = { type: "callback", name: name.value }; |
michael@0 | 384 | all_ws(); |
michael@0 | 385 | consume(OTHER, "=") || error("No assignment in callback"); |
michael@0 | 386 | all_ws(); |
michael@0 | 387 | ret.idlType = return_type(); |
michael@0 | 388 | all_ws(); |
michael@0 | 389 | consume(OTHER, "(") || error("No arguments in callback"); |
michael@0 | 390 | ret["arguments"] = argument_list(); |
michael@0 | 391 | all_ws(); |
michael@0 | 392 | consume(OTHER, ")") || error("Unterminated callback"); |
michael@0 | 393 | all_ws(); |
michael@0 | 394 | consume(OTHER, ";") || error("Unterminated callback"); |
michael@0 | 395 | return ret; |
michael@0 | 396 | }; |
michael@0 | 397 | |
michael@0 | 398 | var attribute = function () { |
michael@0 | 399 | all_ws(); |
michael@0 | 400 | var grabbed = [] |
michael@0 | 401 | , ret = { |
michael@0 | 402 | type: "attribute" |
michael@0 | 403 | , "static": false |
michael@0 | 404 | , stringifier: false |
michael@0 | 405 | , inherit: false |
michael@0 | 406 | , readonly: false |
michael@0 | 407 | }; |
michael@0 | 408 | if (consume(ID, "static")) { |
michael@0 | 409 | ret["static"] = true; |
michael@0 | 410 | grabbed.push(last_token); |
michael@0 | 411 | } |
michael@0 | 412 | else if (consume(ID, "stringifier")) { |
michael@0 | 413 | ret.stringifier = true; |
michael@0 | 414 | grabbed.push(last_token); |
michael@0 | 415 | } |
michael@0 | 416 | var w = all_ws(); |
michael@0 | 417 | if (w) grabbed.push(w); |
michael@0 | 418 | if (consume(ID, "inherit")) { |
michael@0 | 419 | if (ret["static"] || ret.stringifier) error("Cannot have a static or stringifier inherit"); |
michael@0 | 420 | ret.inherit = true; |
michael@0 | 421 | grabbed.push(last_token); |
michael@0 | 422 | var w = all_ws(); |
michael@0 | 423 | if (w) grabbed.push(w); |
michael@0 | 424 | } |
michael@0 | 425 | if (consume(ID, "readonly")) { |
michael@0 | 426 | ret.readonly = true; |
michael@0 | 427 | grabbed.push(last_token); |
michael@0 | 428 | var w = all_ws(); |
michael@0 | 429 | if (w) grabbed.push(w); |
michael@0 | 430 | } |
michael@0 | 431 | if (!consume(ID, "attribute")) { |
michael@0 | 432 | tokens = grabbed.concat(tokens); |
michael@0 | 433 | return; |
michael@0 | 434 | } |
michael@0 | 435 | all_ws(); |
michael@0 | 436 | ret.idlType = type() || error("No type in attribute"); |
michael@0 | 437 | if (ret.idlType.sequence) error("Attributes cannot accept sequence types"); |
michael@0 | 438 | all_ws(); |
michael@0 | 439 | var name = consume(ID) || error("No name in attribute"); |
michael@0 | 440 | ret.name = name.value; |
michael@0 | 441 | all_ws(); |
michael@0 | 442 | consume(OTHER, ";") || error("Unterminated attribute"); |
michael@0 | 443 | return ret; |
michael@0 | 444 | }; |
michael@0 | 445 | |
michael@0 | 446 | var return_type = function () { |
michael@0 | 447 | var typ = type(); |
michael@0 | 448 | if (!typ) { |
michael@0 | 449 | if (consume(ID, "void")) { |
michael@0 | 450 | return "void"; |
michael@0 | 451 | } |
michael@0 | 452 | else error("No return type"); |
michael@0 | 453 | } |
michael@0 | 454 | return typ; |
michael@0 | 455 | }; |
michael@0 | 456 | |
michael@0 | 457 | var operation = function () { |
michael@0 | 458 | all_ws(); |
michael@0 | 459 | var ret = { |
michael@0 | 460 | type: "operation" |
michael@0 | 461 | , getter: false |
michael@0 | 462 | , setter: false |
michael@0 | 463 | , creator: false |
michael@0 | 464 | , deleter: false |
michael@0 | 465 | , legacycaller: false |
michael@0 | 466 | , "static": false |
michael@0 | 467 | , stringifier: false |
michael@0 | 468 | }; |
michael@0 | 469 | while (true) { |
michael@0 | 470 | all_ws(); |
michael@0 | 471 | if (consume(ID, "getter")) ret.getter = true; |
michael@0 | 472 | else if (consume(ID, "setter")) ret.setter = true; |
michael@0 | 473 | else if (consume(ID, "creator")) ret.creator = true; |
michael@0 | 474 | else if (consume(ID, "deleter")) ret.deleter = true; |
michael@0 | 475 | else if (consume(ID, "legacycaller")) ret.legacycaller = true; |
michael@0 | 476 | else break; |
michael@0 | 477 | } |
michael@0 | 478 | if (ret.getter || ret.setter || ret.creator || ret.deleter || ret.legacycaller) { |
michael@0 | 479 | all_ws(); |
michael@0 | 480 | ret.idlType = return_type(); |
michael@0 | 481 | operation_rest(ret); |
michael@0 | 482 | return ret; |
michael@0 | 483 | } |
michael@0 | 484 | if (consume(ID, "static")) { |
michael@0 | 485 | ret["static"] = true; |
michael@0 | 486 | ret.idlType = return_type(); |
michael@0 | 487 | operation_rest(ret); |
michael@0 | 488 | return ret; |
michael@0 | 489 | } |
michael@0 | 490 | else if (consume(ID, "stringifier")) { |
michael@0 | 491 | ret.stringifier = true; |
michael@0 | 492 | all_ws(); |
michael@0 | 493 | if (consume(OTHER, ";")) return ret; |
michael@0 | 494 | ret.idlType = return_type(); |
michael@0 | 495 | operation_rest(ret); |
michael@0 | 496 | return ret; |
michael@0 | 497 | } |
michael@0 | 498 | ret.idlType = return_type(); |
michael@0 | 499 | all_ws(); |
michael@0 | 500 | if (consume(ID, "iterator")) { |
michael@0 | 501 | all_ws(); |
michael@0 | 502 | ret.type = "iterator"; |
michael@0 | 503 | if (consume(ID, "object")) { |
michael@0 | 504 | ret.iteratorObject = "object"; |
michael@0 | 505 | } |
michael@0 | 506 | else if (consume(OTHER, "=")) { |
michael@0 | 507 | all_ws(); |
michael@0 | 508 | var name = consume(ID) || error("No right hand side in iterator"); |
michael@0 | 509 | ret.iteratorObject = name.value; |
michael@0 | 510 | } |
michael@0 | 511 | all_ws(); |
michael@0 | 512 | consume(OTHER, ";") || error("Unterminated iterator"); |
michael@0 | 513 | return ret; |
michael@0 | 514 | } |
michael@0 | 515 | else { |
michael@0 | 516 | operation_rest(ret); |
michael@0 | 517 | return ret; |
michael@0 | 518 | } |
michael@0 | 519 | }; |
michael@0 | 520 | |
michael@0 | 521 | var identifiers = function (arr) { |
michael@0 | 522 | while (true) { |
michael@0 | 523 | all_ws(); |
michael@0 | 524 | if (consume(OTHER, ",")) { |
michael@0 | 525 | all_ws(); |
michael@0 | 526 | var name = consume(ID) || error("Trailing comma in identifiers list"); |
michael@0 | 527 | arr.push(name.value); |
michael@0 | 528 | } |
michael@0 | 529 | else break; |
michael@0 | 530 | } |
michael@0 | 531 | }; |
michael@0 | 532 | |
michael@0 | 533 | var serialiser = function () { |
michael@0 | 534 | all_ws(); |
michael@0 | 535 | if (!consume(ID, "serializer")) return; |
michael@0 | 536 | var ret = { type: "serializer" }; |
michael@0 | 537 | all_ws(); |
michael@0 | 538 | if (consume(OTHER, "=")) { |
michael@0 | 539 | all_ws(); |
michael@0 | 540 | if (consume(OTHER, "{")) { |
michael@0 | 541 | ret.patternMap = true; |
michael@0 | 542 | all_ws(); |
michael@0 | 543 | var id = consume(ID); |
michael@0 | 544 | if (id && id.value === "getter") { |
michael@0 | 545 | ret.names = ["getter"]; |
michael@0 | 546 | } |
michael@0 | 547 | else if (id && id.value === "inherit") { |
michael@0 | 548 | ret.names = ["inherit"]; |
michael@0 | 549 | identifiers(ret.names); |
michael@0 | 550 | } |
michael@0 | 551 | else if (id) { |
michael@0 | 552 | ret.names = [id.value]; |
michael@0 | 553 | identifiers(ret.names); |
michael@0 | 554 | } |
michael@0 | 555 | else { |
michael@0 | 556 | ret.names = []; |
michael@0 | 557 | } |
michael@0 | 558 | all_ws(); |
michael@0 | 559 | consume(OTHER, "}") || error("Unterminated serializer pattern map"); |
michael@0 | 560 | } |
michael@0 | 561 | else if (consume(OTHER, "[")) { |
michael@0 | 562 | ret.patternList = true; |
michael@0 | 563 | all_ws(); |
michael@0 | 564 | var id = consume(ID); |
michael@0 | 565 | if (id && id.value === "getter") { |
michael@0 | 566 | ret.names = ["getter"]; |
michael@0 | 567 | } |
michael@0 | 568 | else if (id) { |
michael@0 | 569 | ret.names = [id.value]; |
michael@0 | 570 | identifiers(ret.names); |
michael@0 | 571 | } |
michael@0 | 572 | else { |
michael@0 | 573 | ret.names = []; |
michael@0 | 574 | } |
michael@0 | 575 | all_ws(); |
michael@0 | 576 | consume(OTHER, "]") || error("Unterminated serializer pattern list"); |
michael@0 | 577 | } |
michael@0 | 578 | else { |
michael@0 | 579 | var name = consume(ID) || error("Invalid serializer"); |
michael@0 | 580 | ret.name = name.value; |
michael@0 | 581 | } |
michael@0 | 582 | all_ws(); |
michael@0 | 583 | consume(OTHER, ";") || error("Unterminated serializer"); |
michael@0 | 584 | return ret; |
michael@0 | 585 | } |
michael@0 | 586 | else if (consume(OTHER, ";")) { |
michael@0 | 587 | // noop, just parsing |
michael@0 | 588 | } |
michael@0 | 589 | else { |
michael@0 | 590 | ret.idlType = return_type(); |
michael@0 | 591 | all_ws(); |
michael@0 | 592 | ret.operation = operation_rest(); |
michael@0 | 593 | } |
michael@0 | 594 | return ret; |
michael@0 | 595 | }; |
michael@0 | 596 | |
michael@0 | 597 | var interface_ = function (isPartial) { |
michael@0 | 598 | all_ws(); |
michael@0 | 599 | if (!consume(ID, "interface")) return; |
michael@0 | 600 | all_ws(); |
michael@0 | 601 | var name = consume(ID) || error("No name for interface"); |
michael@0 | 602 | var ret = { |
michael@0 | 603 | type: "interface" |
michael@0 | 604 | , name: name.value |
michael@0 | 605 | , partial: false |
michael@0 | 606 | , members: [] |
michael@0 | 607 | }; |
michael@0 | 608 | if (!isPartial) ret.inheritance = inheritance() || null; |
michael@0 | 609 | all_ws(); |
michael@0 | 610 | consume(OTHER, "{") || error("Bodyless interface"); |
michael@0 | 611 | while (true) { |
michael@0 | 612 | all_ws(); |
michael@0 | 613 | if (consume(OTHER, "}")) { |
michael@0 | 614 | all_ws(); |
michael@0 | 615 | consume(OTHER, ";") || error("Missing semicolon after interface"); |
michael@0 | 616 | return ret; |
michael@0 | 617 | } |
michael@0 | 618 | var ea = extended_attrs(); |
michael@0 | 619 | all_ws(); |
michael@0 | 620 | var cnt = const_(); |
michael@0 | 621 | if (cnt) { |
michael@0 | 622 | cnt.extAttrs = ea; |
michael@0 | 623 | ret.members.push(cnt); |
michael@0 | 624 | continue; |
michael@0 | 625 | } |
michael@0 | 626 | var mem = serialiser() || attribute() || operation() || error("Unknown member"); |
michael@0 | 627 | mem.extAttrs = ea; |
michael@0 | 628 | ret.members.push(mem); |
michael@0 | 629 | } |
michael@0 | 630 | }; |
michael@0 | 631 | |
michael@0 | 632 | var partial = function () { |
michael@0 | 633 | all_ws(); |
michael@0 | 634 | if (!consume(ID, "partial")) return; |
michael@0 | 635 | var thing = dictionary(true) || interface_(true) || error("Partial doesn't apply to anything"); |
michael@0 | 636 | thing.partial = true; |
michael@0 | 637 | return thing; |
michael@0 | 638 | }; |
michael@0 | 639 | |
michael@0 | 640 | var dictionary = function (isPartial) { |
michael@0 | 641 | all_ws(); |
michael@0 | 642 | if (!consume(ID, "dictionary")) return; |
michael@0 | 643 | all_ws(); |
michael@0 | 644 | var name = consume(ID) || error("No name for dictionary"); |
michael@0 | 645 | var ret = { |
michael@0 | 646 | type: "dictionary" |
michael@0 | 647 | , name: name.value |
michael@0 | 648 | , partial: false |
michael@0 | 649 | , members: [] |
michael@0 | 650 | }; |
michael@0 | 651 | if (!isPartial) ret.inheritance = inheritance() || null; |
michael@0 | 652 | all_ws(); |
michael@0 | 653 | consume(OTHER, "{") || error("Bodyless dictionary"); |
michael@0 | 654 | while (true) { |
michael@0 | 655 | all_ws(); |
michael@0 | 656 | if (consume(OTHER, "}")) { |
michael@0 | 657 | all_ws(); |
michael@0 | 658 | consume(OTHER, ";") || error("Missing semicolon after dictionary"); |
michael@0 | 659 | return ret; |
michael@0 | 660 | } |
michael@0 | 661 | var ea = extended_attrs(); |
michael@0 | 662 | all_ws(); |
michael@0 | 663 | var typ = type() || error("No type for dictionary member"); |
michael@0 | 664 | all_ws(); |
michael@0 | 665 | var name = consume(ID) || error("No name for dictionary member"); |
michael@0 | 666 | ret.members.push({ |
michael@0 | 667 | type: "field" |
michael@0 | 668 | , name: name.value |
michael@0 | 669 | , idlType: typ |
michael@0 | 670 | , extAttrs: ea |
michael@0 | 671 | , "default": default_() |
michael@0 | 672 | }); |
michael@0 | 673 | all_ws(); |
michael@0 | 674 | consume(OTHER, ";") || error("Unterminated dictionary member"); |
michael@0 | 675 | } |
michael@0 | 676 | }; |
michael@0 | 677 | |
michael@0 | 678 | var exception = function () { |
michael@0 | 679 | all_ws(); |
michael@0 | 680 | if (!consume(ID, "exception")) return; |
michael@0 | 681 | all_ws(); |
michael@0 | 682 | var name = consume(ID) || error("No name for exception"); |
michael@0 | 683 | var ret = { |
michael@0 | 684 | type: "exception" |
michael@0 | 685 | , name: name.value |
michael@0 | 686 | , members: [] |
michael@0 | 687 | }; |
michael@0 | 688 | ret.inheritance = inheritance() || null; |
michael@0 | 689 | all_ws(); |
michael@0 | 690 | consume(OTHER, "{") || error("Bodyless exception"); |
michael@0 | 691 | while (true) { |
michael@0 | 692 | all_ws(); |
michael@0 | 693 | if (consume(OTHER, "}")) { |
michael@0 | 694 | all_ws(); |
michael@0 | 695 | consume(OTHER, ";") || error("Missing semicolon after exception"); |
michael@0 | 696 | return ret; |
michael@0 | 697 | } |
michael@0 | 698 | var ea = extended_attrs(); |
michael@0 | 699 | all_ws(); |
michael@0 | 700 | var cnt = const_(); |
michael@0 | 701 | if (cnt) { |
michael@0 | 702 | cnt.extAttrs = ea; |
michael@0 | 703 | ret.members.push(cnt); |
michael@0 | 704 | } |
michael@0 | 705 | else { |
michael@0 | 706 | var typ = type(); |
michael@0 | 707 | all_ws(); |
michael@0 | 708 | var name = consume(ID); |
michael@0 | 709 | all_ws(); |
michael@0 | 710 | if (!typ || !name || !consume(OTHER, ";")) error("Unknown member in exception body"); |
michael@0 | 711 | ret.members.push({ |
michael@0 | 712 | type: "field" |
michael@0 | 713 | , name: name.value |
michael@0 | 714 | , idlType: typ |
michael@0 | 715 | , extAttrs: ea |
michael@0 | 716 | }); |
michael@0 | 717 | } |
michael@0 | 718 | } |
michael@0 | 719 | }; |
michael@0 | 720 | |
michael@0 | 721 | var enum_ = function () { |
michael@0 | 722 | all_ws(); |
michael@0 | 723 | if (!consume(ID, "enum")) return; |
michael@0 | 724 | all_ws(); |
michael@0 | 725 | var name = consume(ID) || error("No name for enum"); |
michael@0 | 726 | var ret = { |
michael@0 | 727 | type: "enum" |
michael@0 | 728 | , name: name.value |
michael@0 | 729 | , values: [] |
michael@0 | 730 | }; |
michael@0 | 731 | all_ws(); |
michael@0 | 732 | consume(OTHER, "{") || error("No curly for enum"); |
michael@0 | 733 | var saw_comma = false; |
michael@0 | 734 | while (true) { |
michael@0 | 735 | all_ws(); |
michael@0 | 736 | if (consume(OTHER, "}")) { |
michael@0 | 737 | all_ws(); |
michael@0 | 738 | if (saw_comma) error("Trailing comma in enum"); |
michael@0 | 739 | consume(OTHER, ";") || error("No semicolon after enum"); |
michael@0 | 740 | return ret; |
michael@0 | 741 | } |
michael@0 | 742 | var val = consume(STR) || error("Unexpected value in enum"); |
michael@0 | 743 | ret.values.push(val.value.replace(/"/g, "")); |
michael@0 | 744 | all_ws(); |
michael@0 | 745 | if (consume(OTHER, ",")) { |
michael@0 | 746 | all_ws(); |
michael@0 | 747 | saw_comma = true; |
michael@0 | 748 | } |
michael@0 | 749 | else { |
michael@0 | 750 | saw_comma = false; |
michael@0 | 751 | } |
michael@0 | 752 | } |
michael@0 | 753 | }; |
michael@0 | 754 | |
michael@0 | 755 | var typedef = function () { |
michael@0 | 756 | all_ws(); |
michael@0 | 757 | if (!consume(ID, "typedef")) return; |
michael@0 | 758 | var ret = { |
michael@0 | 759 | type: "typedef" |
michael@0 | 760 | }; |
michael@0 | 761 | all_ws(); |
michael@0 | 762 | ret.typeExtAttrs = extended_attrs(); |
michael@0 | 763 | all_ws(); |
michael@0 | 764 | ret.idlType = type() || error("No type in typedef"); |
michael@0 | 765 | all_ws(); |
michael@0 | 766 | var name = consume(ID) || error("No name in typedef"); |
michael@0 | 767 | ret.name = name.value; |
michael@0 | 768 | all_ws(); |
michael@0 | 769 | consume(OTHER, ";") || error("Unterminated typedef"); |
michael@0 | 770 | return ret; |
michael@0 | 771 | }; |
michael@0 | 772 | |
michael@0 | 773 | var implements_ = function () { |
michael@0 | 774 | all_ws(); |
michael@0 | 775 | var target = consume(ID); |
michael@0 | 776 | if (!target) return; |
michael@0 | 777 | var w = all_ws(); |
michael@0 | 778 | if (consume(ID, "implements")) { |
michael@0 | 779 | var ret = { |
michael@0 | 780 | type: "implements" |
michael@0 | 781 | , target: target.value |
michael@0 | 782 | }; |
michael@0 | 783 | all_ws(); |
michael@0 | 784 | var imp = consume(ID) || error("Incomplete implements statement"); |
michael@0 | 785 | ret["implements"] = imp.value; |
michael@0 | 786 | all_ws(); |
michael@0 | 787 | consume(OTHER, ";") || error("No terminating ; for implements statement"); |
michael@0 | 788 | return ret; |
michael@0 | 789 | } |
michael@0 | 790 | else { |
michael@0 | 791 | // rollback |
michael@0 | 792 | tokens.unshift(w); |
michael@0 | 793 | tokens.unshift(target); |
michael@0 | 794 | } |
michael@0 | 795 | }; |
michael@0 | 796 | |
michael@0 | 797 | var definition = function () { |
michael@0 | 798 | return callback() || |
michael@0 | 799 | interface_() || |
michael@0 | 800 | partial() || |
michael@0 | 801 | dictionary() || |
michael@0 | 802 | exception() || |
michael@0 | 803 | enum_() || |
michael@0 | 804 | typedef() || |
michael@0 | 805 | implements_() |
michael@0 | 806 | ; |
michael@0 | 807 | }; |
michael@0 | 808 | |
michael@0 | 809 | var definitions = function () { |
michael@0 | 810 | if (!tokens.length) return []; |
michael@0 | 811 | var defs = []; |
michael@0 | 812 | while (true) { |
michael@0 | 813 | var ea = extended_attrs() |
michael@0 | 814 | , def = definition(); |
michael@0 | 815 | if (!def) { |
michael@0 | 816 | if (ea.length) error("Stray extended attributes"); |
michael@0 | 817 | break; |
michael@0 | 818 | } |
michael@0 | 819 | def.extAttrs = ea; |
michael@0 | 820 | defs.push(def); |
michael@0 | 821 | } |
michael@0 | 822 | return defs; |
michael@0 | 823 | }; |
michael@0 | 824 | var res = definitions(); |
michael@0 | 825 | if (tokens.length) error("Unrecognised tokens"); |
michael@0 | 826 | return res; |
michael@0 | 827 | }; |
michael@0 | 828 | |
michael@0 | 829 | var obj = { |
michael@0 | 830 | parse: function (str) { |
michael@0 | 831 | var tokens = tokenise(str); |
michael@0 | 832 | return parse(tokens); |
michael@0 | 833 | } |
michael@0 | 834 | }; |
michael@0 | 835 | if (typeof module !== "undefined" && module.exports) { |
michael@0 | 836 | module.exports = obj; |
michael@0 | 837 | } |
michael@0 | 838 | else { |
michael@0 | 839 | window.WebIDL2 = obj; |
michael@0 | 840 | } |
michael@0 | 841 | }()); |