2014-09-24 10:50:23 +04:00
|
|
|
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
|
|
|
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
var Override = {};
|
|
|
|
|
2014-10-14 20:42:01 +04:00
|
|
|
function JavaException(className, message) {
|
|
|
|
this.javaClassName = className;
|
|
|
|
this.message = message;
|
2014-09-24 23:55:17 +04:00
|
|
|
}
|
2014-10-14 20:42:01 +04:00
|
|
|
JavaException.prototype = Object.create(Error.prototype);
|
2014-09-24 23:55:17 +04:00
|
|
|
|
2014-10-14 20:42:01 +04:00
|
|
|
/**
|
|
|
|
* A simple wrapper for overriding JVM functions to avoid logic errors
|
|
|
|
* and simplify implementation:
|
|
|
|
*
|
|
|
|
* - Arguments are pushed off the stack based upon the number of
|
|
|
|
* arguments listed on `fn`.
|
|
|
|
*
|
|
|
|
* - The return value is automatically pushed back onto the stack, if
|
|
|
|
* the method signature does not return void. CAUTION: If you want to
|
|
|
|
* return a Long or Double, this code needs to be modified
|
|
|
|
* accordingly to do a `push2`. (Ideally, we'd just scrape the
|
|
|
|
* method signature and always do the right thing.)
|
|
|
|
*
|
|
|
|
* - The object reference ("this") is automatically bound to `fn`,
|
|
|
|
* unless you specify { static: true } in opts.
|
|
|
|
*
|
|
|
|
* - JavaException instances are caught and propagated as Java
|
|
|
|
exceptions; JS TypeError propagates as a NullPointerException.
|
|
|
|
*
|
|
|
|
* @param {string} key
|
|
|
|
* The fully-qualified JVM method signature.
|
|
|
|
* @param {function(args)} fn
|
|
|
|
* A function taking any number of args. The number of arguments
|
|
|
|
* this function takes is the number of args popped off of the stack.
|
|
|
|
* @param {object} opts
|
|
|
|
* { static: true } if the method is static (and should not receive
|
|
|
|
* and pop the `this` argument off the stack).
|
|
|
|
*/
|
2014-10-15 03:38:16 +04:00
|
|
|
function createAlternateImpl(object, key, fn) {
|
2014-10-14 21:29:34 +04:00
|
|
|
var retType = key[key.length - 1];
|
2014-10-14 20:42:01 +04:00
|
|
|
var numArgs = fn.length;
|
2014-10-15 03:38:16 +04:00
|
|
|
object[key] = function(ctx, stack, isStatic) {
|
2014-10-14 20:42:01 +04:00
|
|
|
var args = new Array(numArgs);
|
2014-10-14 21:29:34 +04:00
|
|
|
|
|
|
|
args[0] = ctx;
|
|
|
|
|
2014-10-14 20:42:01 +04:00
|
|
|
// NOTE: If your function accepts a Long/Double, you must specify
|
|
|
|
// two arguments (since they take up two stack positions); we
|
|
|
|
// could sugar this someday.
|
2014-10-14 21:29:34 +04:00
|
|
|
for (var i = numArgs - 1; i >= 1; i--) {
|
2014-10-14 20:42:01 +04:00
|
|
|
args[i] = stack.pop();
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
var self = isStatic ? null : stack.pop();
|
2014-10-14 21:29:34 +04:00
|
|
|
var ret = fn.apply(self, args);
|
|
|
|
if (retType !== 'V') {
|
2014-10-14 20:42:01 +04:00
|
|
|
if (ret === true) {
|
|
|
|
stack.push(1);
|
|
|
|
} else if (ret === false) {
|
|
|
|
stack.push(0);
|
|
|
|
} else if (typeof ret === "string") {
|
|
|
|
stack.push(ctx.newString(ret));
|
2014-10-14 22:26:29 +04:00
|
|
|
} else if (retType === 'J' || retType === 'D') {
|
2014-10-14 21:29:34 +04:00
|
|
|
stack.push2(ret);
|
2014-10-14 20:42:01 +04:00
|
|
|
} else {
|
|
|
|
stack.push(ret);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch(e) {
|
2014-10-14 22:24:33 +04:00
|
|
|
if (e === VM.Pause || e === VM.Yield) {
|
|
|
|
throw e;
|
|
|
|
} else if (e.name === "TypeError") {
|
2014-10-14 20:42:01 +04:00
|
|
|
// JavaScript's TypeError is analogous to a NullPointerException.
|
|
|
|
ctx.raiseExceptionAndYield("java/lang/NullPointerException", e);
|
|
|
|
} else if (e.javaClassName) {
|
|
|
|
ctx.raiseExceptionAndYield(e.javaClassName, e.message);
|
|
|
|
} else {
|
|
|
|
console.error(e, e.stack);
|
|
|
|
ctx.raiseExceptionAndYield("java/lang/RuntimeException", e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2014-09-24 12:10:39 +04:00
|
|
|
}
|
2014-09-25 23:14:32 +04:00
|
|
|
|
2014-10-14 21:29:34 +04:00
|
|
|
Override.create = createAlternateImpl.bind(null, Override);
|
2014-10-14 20:42:01 +04:00
|
|
|
|
2014-10-14 21:29:34 +04:00
|
|
|
Override.create("com/ibm/oti/connection/file/Connection.decode.(Ljava/lang/String;)Ljava/lang/String;", function(ctx, string) {
|
2014-10-14 20:42:01 +04:00
|
|
|
return decodeURIComponent(string.str);
|
2014-10-15 03:38:16 +04:00
|
|
|
});
|
2014-10-14 20:42:01 +04:00
|
|
|
|
2014-10-14 21:29:34 +04:00
|
|
|
Override.create("com/ibm/oti/connection/file/Connection.encode.(Ljava/lang/String;)Ljava/lang/String;", function(ctx, string) {
|
2014-10-14 20:42:01 +04:00
|
|
|
return string.str.replace(/[^a-zA-Z0-9-_\.!~\*\\'()/:]/g, encodeURIComponent);
|
2014-10-15 03:38:16 +04:00
|
|
|
});
|
2014-10-14 20:42:01 +04:00
|
|
|
|
2014-10-14 21:29:34 +04:00
|
|
|
Override.create("java/lang/Math.min.(II)I", function(ctx, a, b) {
|
2014-10-14 20:42:01 +04:00
|
|
|
return Math.min(a, b);
|
2014-10-15 03:38:16 +04:00
|
|
|
});
|
2014-09-26 21:37:25 +04:00
|
|
|
|
2014-10-14 21:29:34 +04:00
|
|
|
Override.create("java/io/ByteArrayOutputStream.write.([BII)V", function(ctx, b, off, len) {
|
2014-10-14 20:42:01 +04:00
|
|
|
if ((off < 0) || (off > b.length) || (len < 0) ||
|
|
|
|
((off + len) > b.length)) {
|
|
|
|
throw new JavaException("java/lang/IndexOutOfBoundsException");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var count = this.class.getField("I.count.I").get(this);
|
|
|
|
var buf = this.class.getField("I.buf.[B").get(this);
|
|
|
|
|
|
|
|
var newcount = count + len;
|
|
|
|
if (newcount > buf.length) {
|
|
|
|
var newbuf = ctx.newPrimitiveArray("B", Math.max(buf.length << 1, newcount));
|
|
|
|
newbuf.set(buf);
|
|
|
|
buf = newbuf;
|
|
|
|
this.class.getField("I.buf.[B").set(this, buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
buf.set(b.subarray(off, off + len), count);
|
|
|
|
this.class.getField("I.count.I").set(this, newcount);
|
|
|
|
});
|
|
|
|
|
2014-10-14 21:29:34 +04:00
|
|
|
Override.create("java/io/ByteArrayOutputStream.write.(I)V", function(ctx, value) {
|
|
|
|
var count = this.class.getField("I.count.I").get(this);
|
|
|
|
var buf = this.class.getField("I.buf.[B").get(this);
|
2014-09-30 20:49:36 +04:00
|
|
|
|
|
|
|
var newcount = count + 1;
|
|
|
|
if (newcount > buf.length) {
|
|
|
|
var newbuf = ctx.newPrimitiveArray("B", Math.max(buf.length << 1, newcount));
|
|
|
|
newbuf.set(buf);
|
|
|
|
buf = newbuf;
|
2014-10-14 21:29:34 +04:00
|
|
|
this.class.getField("I.buf.[B").set(this, buf);
|
2014-09-30 20:49:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
buf[count] = value;
|
2014-10-14 21:29:34 +04:00
|
|
|
this.class.getField("I.count.I").set(this, newcount);
|
|
|
|
});
|
2014-09-26 05:14:38 +04:00
|
|
|
|
2014-10-14 21:29:34 +04:00
|
|
|
Override.create("java/io/ByteArrayInputStream.<init>.([B)V", function(ctx, buf) {
|
2014-09-26 05:14:38 +04:00
|
|
|
if (!buf) {
|
2014-10-14 21:29:34 +04:00
|
|
|
throw new JavaException("java/lang/NullPointerException");
|
2014-09-26 05:14:38 +04:00
|
|
|
}
|
|
|
|
|
2014-10-14 21:29:34 +04:00
|
|
|
this.buf = buf;
|
|
|
|
this.pos = this.mark = 0;
|
|
|
|
this.count = buf.length;
|
|
|
|
});
|
2014-09-26 05:14:38 +04:00
|
|
|
|
2014-10-14 21:29:34 +04:00
|
|
|
Override.create("java/io/ByteArrayInputStream.<init>.([BII)V", function(ctx, buf, offset, length) {
|
2014-09-26 05:14:38 +04:00
|
|
|
if (!buf) {
|
2014-10-14 21:29:34 +04:00
|
|
|
throw new JavaException("java/lang/NullPointerException");
|
2014-09-26 05:14:38 +04:00
|
|
|
}
|
|
|
|
|
2014-10-14 21:29:34 +04:00
|
|
|
this.buf = buf;
|
|
|
|
this.pos = this.mark = offset;
|
|
|
|
this.count = (offset + length <= buf.length) ? (offset + length) : buf.length;
|
|
|
|
});
|
2014-09-26 05:14:38 +04:00
|
|
|
|
2014-10-14 21:29:34 +04:00
|
|
|
Override.create("java/io/ByteArrayInputStream.read.()I", function(ctx) {
|
|
|
|
return (this.pos < this.count) ? (this.buf[this.pos++] & 0xFF) : -1;
|
|
|
|
});
|
2014-09-26 05:14:38 +04:00
|
|
|
|
2014-10-14 21:29:34 +04:00
|
|
|
Override.create("java/io/ByteArrayInputStream.read.([BII)I", function(ctx, b, off, len) {
|
2014-09-26 05:14:38 +04:00
|
|
|
if (!b) {
|
2014-10-14 21:29:34 +04:00
|
|
|
throw new JavaException("java/lang/NullPointerException");
|
2014-09-27 07:58:43 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((off < 0) || (off > b.length) || (len < 0) ||
|
|
|
|
((off + len) > b.length)) {
|
2014-10-14 21:29:34 +04:00
|
|
|
throw new JavaException("java/lang/IndexOutOfBoundsException");
|
2014-09-26 05:14:38 +04:00
|
|
|
}
|
|
|
|
|
2014-10-14 21:29:34 +04:00
|
|
|
if (this.pos >= this.count) {
|
|
|
|
return -1;
|
2014-09-26 05:14:38 +04:00
|
|
|
}
|
2014-10-14 21:29:34 +04:00
|
|
|
if (this.pos + len > this.count) {
|
|
|
|
len = this.count - this.pos;
|
2014-09-26 05:14:38 +04:00
|
|
|
}
|
2014-09-27 07:58:43 +04:00
|
|
|
if (len === 0) {
|
2014-10-14 21:29:34 +04:00
|
|
|
return 0;
|
2014-09-26 05:14:38 +04:00
|
|
|
}
|
|
|
|
|
2014-10-14 21:29:34 +04:00
|
|
|
b.set(this.buf.subarray(this.pos, this.pos + len), off);
|
2014-09-26 05:14:38 +04:00
|
|
|
|
2014-10-14 21:29:34 +04:00
|
|
|
this.pos += len;
|
|
|
|
return len;
|
|
|
|
});
|
2014-09-26 05:14:38 +04:00
|
|
|
|
2014-10-14 21:29:34 +04:00
|
|
|
Override.create("java/io/ByteArrayInputStream.skip.(J)J", function(ctx, long, _) {
|
|
|
|
var n = long.toNumber();
|
2014-09-26 04:06:43 +04:00
|
|
|
|
2014-10-14 21:29:34 +04:00
|
|
|
if (this.pos + n > this.count) {
|
|
|
|
n = this.count - this.pos;
|
2014-09-26 04:06:43 +04:00
|
|
|
}
|
2014-09-26 05:14:38 +04:00
|
|
|
|
|
|
|
if (n < 0) {
|
2014-10-14 21:29:34 +04:00
|
|
|
return Long.fromNumber(0);
|
2014-09-26 05:14:38 +04:00
|
|
|
}
|
|
|
|
|
2014-10-14 21:29:34 +04:00
|
|
|
this.pos += n;
|
2014-09-26 05:14:38 +04:00
|
|
|
|
2014-10-14 21:29:34 +04:00
|
|
|
return Long.fromNumber(n);
|
|
|
|
});
|
2014-09-26 05:14:38 +04:00
|
|
|
|
2014-10-14 21:29:34 +04:00
|
|
|
Override.create("java/io/ByteArrayInputStream.available.()I", function(ctx) {
|
|
|
|
return this.count - this.pos;
|
|
|
|
});
|
2014-09-26 05:14:38 +04:00
|
|
|
|
2014-10-14 21:29:34 +04:00
|
|
|
Override.create("java/io/ByteArrayInputStream.mark.(I)V", function(ctx, readAheadLimit) {
|
|
|
|
this.mark = this.pos;
|
|
|
|
});
|
2014-09-26 05:14:38 +04:00
|
|
|
|
2014-10-14 21:29:34 +04:00
|
|
|
Override.create("java/io/ByteArrayInputStream.reset.()V", function(ctx) {
|
|
|
|
this.pos = this.mark;
|
|
|
|
});
|
2014-09-26 21:57:15 +04:00
|
|
|
|
2014-10-01 06:41:50 +04:00
|
|
|
// The following Permissions methods are overriden to avoid expensive calls to
|
|
|
|
// DomainPolicy.loadValues. This has the added benefit that we avoid many other
|
|
|
|
// computations.
|
|
|
|
|
2014-10-14 21:29:34 +04:00
|
|
|
Override.create("com/sun/midp/security/Permissions.forDomain.(Ljava/lang/String;)[[B", function(ctx, name) {
|
2014-09-30 22:13:40 +04:00
|
|
|
// NUMBER_OF_PERMISSIONS = PermissionsStrings.PERMISSION_STRINGS.length + 2
|
2014-10-01 06:29:25 +04:00
|
|
|
// The 2 is the two hardcoded MIPS and AMS permissions.
|
2014-09-30 22:13:40 +04:00
|
|
|
var NUMBER_OF_PERMISSIONS = 61;
|
|
|
|
var ALLOW = 1;
|
|
|
|
|
2014-10-01 06:30:09 +04:00
|
|
|
var maximums = ctx.newPrimitiveArray("B", NUMBER_OF_PERMISSIONS);
|
2014-09-30 22:13:40 +04:00
|
|
|
var defaults = ctx.newPrimitiveArray("B", NUMBER_OF_PERMISSIONS);
|
|
|
|
|
|
|
|
for (var i = 0; i < NUMBER_OF_PERMISSIONS; i++) {
|
2014-10-01 06:30:09 +04:00
|
|
|
maximums[i] = defaults[i] = ALLOW;
|
2014-09-30 22:13:40 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
var permissions = ctx.newArray("[[B", 2);
|
2014-10-01 06:30:09 +04:00
|
|
|
permissions[0] = maximums;
|
2014-10-01 06:41:50 +04:00
|
|
|
permissions[1] = defaults;
|
2014-09-30 22:13:40 +04:00
|
|
|
|
2014-10-14 21:29:34 +04:00
|
|
|
return permissions;
|
2014-10-15 03:38:16 +04:00
|
|
|
});
|
2014-09-30 22:13:40 +04:00
|
|
|
|
2014-10-01 06:41:50 +04:00
|
|
|
// Always return true to make Java think the MIDlet domain is trusted.
|
2014-10-14 21:29:34 +04:00
|
|
|
Override.create("com/sun/midp/security/Permissions.isTrusted.(Ljava/lang/String;)Z", function(ctx, name) {
|
2014-10-15 01:55:10 +04:00
|
|
|
return true;
|
2014-10-15 03:38:16 +04:00
|
|
|
});
|
2014-09-30 22:13:40 +04:00
|
|
|
|
2014-10-01 06:41:50 +04:00
|
|
|
// Returns the ID of the permission. The callers will use this ID to check the
|
|
|
|
// permission in the permissions array returned by Permissions::forDomain.
|
2014-10-14 21:29:34 +04:00
|
|
|
Override.create("com/sun/midp/security/Permissions.getId.(Ljava/lang/String;)I", function(ctx, name) {
|
|
|
|
return 0;
|
2014-10-15 03:38:16 +04:00
|
|
|
});
|
2014-09-30 22:13:40 +04:00
|
|
|
|
2014-10-01 06:41:50 +04:00
|
|
|
// The Java code that uses this method doesn't actually use the return value, but
|
|
|
|
// passes it to Permissions.getId. So we can return anything.
|
2014-10-14 21:29:34 +04:00
|
|
|
Override.create("com/sun/midp/security/Permissions.getName.(I)Ljava/lang/String;", function(ctx, id) {
|
|
|
|
return "com.sun.midp";
|
2014-10-15 03:38:16 +04:00
|
|
|
});
|