зеркало из https://github.com/mozilla/pluotsorbet.git
798 строки
26 KiB
JavaScript
798 строки
26 KiB
JavaScript
/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/* vim: set shiftwidth=4 tabstop=4 autoindent cindent expandtab: */
|
|
|
|
'use strict';
|
|
|
|
var asyncImpl = J2ME.asyncImplOld;
|
|
|
|
function preemptingImpl(returnKind, returnValue) {
|
|
if (J2ME.Scheduler.shouldPreempt()) {
|
|
asyncImpl(returnKind, Promise.resolve(returnValue));
|
|
return;
|
|
}
|
|
return returnValue;
|
|
}
|
|
|
|
var Override = {};
|
|
|
|
/**
|
|
* A map from Java object addresses to native objects.
|
|
*
|
|
* Use getNative and setNative to simplify accessing this map. getNative takes
|
|
* a handle, so callers don't need to dereference its address, while setNative
|
|
* returns the native, so callers can chain it with a local variable assignment.
|
|
*
|
|
* Currently this only supports mapping an address to a single native.
|
|
* Will we ever want to map multiple natives to an address? If so, we'll need
|
|
* to do something more sophisticated here.
|
|
*
|
|
* XXX Figure out how to collect a native when its Java object is collected.
|
|
*/
|
|
var NativeMap = new Map();
|
|
|
|
function getNative(javaObj) {
|
|
return NativeMap.get(javaObj._address);
|
|
}
|
|
|
|
function setNative(javaObj, nativeObj) {
|
|
NativeMap.set(javaObj._address, nativeObj);
|
|
return nativeObj;
|
|
}
|
|
|
|
function deleteNative(javaObj) {
|
|
NativeMap.delete(javaObj._address);
|
|
}
|
|
|
|
Native["java/lang/System.arraycopy.(Ljava/lang/Object;ILjava/lang/Object;II)V"] = function(src, srcOffset, dst, dstOffset, length) {
|
|
if (!src || !dst) {
|
|
throw $.newNullPointerException("Cannot copy to/from a null array.");
|
|
}
|
|
|
|
var srcKlass = src.klass;
|
|
var dstKlass = dst.klass;
|
|
|
|
if (!srcKlass.isArrayKlass || !dstKlass.isArrayKlass) {
|
|
throw $.newArrayStoreException("Can only copy to/from array types.");
|
|
}
|
|
if (srcOffset < 0 || (srcOffset+length) > src.length || dstOffset < 0 || (dstOffset+length) > dst.length || length < 0) {
|
|
throw $.newArrayIndexOutOfBoundsException("Invalid index.");
|
|
}
|
|
var srcIsPrimitive = srcKlass.classInfo instanceof J2ME.PrimitiveArrayClassInfo;
|
|
var dstIsPrimitive = dstKlass.classInfo instanceof J2ME.PrimitiveArrayClassInfo;
|
|
if ((srcIsPrimitive && dstIsPrimitive && srcKlass !== dstKlass) ||
|
|
(srcIsPrimitive && !dstIsPrimitive) ||
|
|
(!srcIsPrimitive && dstIsPrimitive)) {
|
|
throw $.newArrayStoreException("Incompatible component types: " + srcKlass + " -> " + dstKlass);
|
|
}
|
|
|
|
if (!dstIsPrimitive) {
|
|
if (srcKlass != dstKlass && !J2ME.isAssignableTo(srcKlass.elementKlass, dstKlass.elementKlass)) {
|
|
var copy = function(to, from) {
|
|
var addr = src[from];
|
|
var obj = J2ME.getArrayFromAddr(addr);
|
|
if (!obj) {
|
|
obj = getHandle(addr);
|
|
}
|
|
if (obj && !J2ME.isAssignableTo(obj.klass, dstKlass.elementKlass)) {
|
|
throw $.newArrayStoreException("Incompatible component types.");
|
|
}
|
|
dst[to] = addr;
|
|
};
|
|
if (dst !== src || dstOffset < srcOffset) {
|
|
for (var n = 0; n < length; ++n)
|
|
copy(dstOffset++, srcOffset++);
|
|
} else {
|
|
dstOffset += length;
|
|
srcOffset += length;
|
|
for (var n = 0; n < length; ++n)
|
|
copy(--dstOffset, --srcOffset);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (dst !== src || dstOffset < srcOffset) {
|
|
for (var n = 0; n < length; ++n)
|
|
dst[dstOffset++] = src[srcOffset++];
|
|
} else {
|
|
dstOffset += length;
|
|
srcOffset += length;
|
|
for (var n = 0; n < length; ++n)
|
|
dst[--dstOffset] = src[--srcOffset];
|
|
}
|
|
};
|
|
|
|
var stubProperties = {
|
|
"com.nokia.multisim.slots": "1",
|
|
"com.nokia.mid.imsi": "000000000000000",
|
|
"com.nokia.mid.imei": "",
|
|
};
|
|
|
|
Native["java/lang/System.getProperty0.(Ljava/lang/String;)Ljava/lang/String;"] = function(key) {
|
|
key = J2ME.fromJavaString(key);
|
|
var value;
|
|
switch (key) {
|
|
case "microedition.encoding":
|
|
// The value of this property is different than the value on a real Nokia Asha 503 phone.
|
|
// On the phone, it is: ISO8859_1.
|
|
// If we changed this, we would need to remove the optimizations for UTF_8_Reader and
|
|
// UTF_8_Writer and optimize the ISO8859_1 alternatives.
|
|
value = "UTF-8";
|
|
break;
|
|
case "microedition.io.file.FileConnection.version":
|
|
value = "1.0";
|
|
break;
|
|
case "microedition.locale":
|
|
value = navigator.language;
|
|
break;
|
|
case "microedition.platform":
|
|
value = config.platform ? config.platform : "Nokia503/14.0.4/java_runtime_version=Nokia_Asha_1_2";
|
|
break;
|
|
case "microedition.platformimpl":
|
|
value = null;
|
|
break;
|
|
case "microedition.profiles":
|
|
value = "MIDP-2.1"
|
|
break;
|
|
case "microedition.pim.version":
|
|
value = "1.0";
|
|
break;
|
|
case "microedition.amms.version":
|
|
value = "1.1";
|
|
break;
|
|
case "microedition.media.version":
|
|
value = '1.2';
|
|
break;
|
|
case "mmapi-configuration":
|
|
value = null;
|
|
break;
|
|
case "fileconn.dir.memorycard":
|
|
value = "file:///MemoryCard/";
|
|
break;
|
|
// The names here should be localized.
|
|
case "fileconn.dir.memorycard.name":
|
|
value = "Memory card";
|
|
break;
|
|
case "fileconn.dir.private":
|
|
value = "file:///Private/";
|
|
break;
|
|
case "fileconn.dir.private.name":
|
|
value = "Private";
|
|
break;
|
|
case "fileconn.dir.applications.bookmarks":
|
|
value = null;
|
|
break;
|
|
case "fileconn.dir.received":
|
|
value = "file:///Phone/_my_downloads/";
|
|
break;
|
|
case "fileconn.dir.received.name":
|
|
value = "Downloads";
|
|
break;
|
|
case "fileconn.dir.photos":
|
|
value = "file:///Phone/_my_pictures/";
|
|
break;
|
|
case "fileconn.dir.photos.name":
|
|
value = "Photos";
|
|
break;
|
|
case "fileconn.dir.videos":
|
|
value = "file:///Phone/_my_videos/";
|
|
break;
|
|
case "fileconn.dir.videos.name":
|
|
value = "Videos";
|
|
break;
|
|
case "fileconn.dir.recordings":
|
|
value = "file:///Phone/_my_recordings/";
|
|
break;
|
|
case "fileconn.dir.recordings.name":
|
|
value = "Recordings";
|
|
break;
|
|
case "fileconn.dir.roots.names":
|
|
value = MIDP.fsRootNames.join(";");
|
|
break;
|
|
case "fileconn.dir.roots.external":
|
|
value = MIDP.fsRoots.map(function(v) { return "file:///" + v }).join("\n");
|
|
break;
|
|
case "file.separator":
|
|
value = "/";
|
|
break;
|
|
case "com.sun.cldc.util.j2me.TimeZoneImpl.timezone":
|
|
// Date.toString() returns something like the following:
|
|
// "Wed Sep 17 2014 12:11:23 GMT-0700 (PDT)"
|
|
//
|
|
// Per http://www.spectrum3847.org/frc2013api/com/sun/cldc/util/j2me/TimeZoneImpl.html,
|
|
// timezones can be of the format GMT+0600, which is what this
|
|
// regex currently matches. (Those actually in GMT would not
|
|
// match the regex, causing the default "GMT" to be returned.)
|
|
// If we find this to be a problem, we could alternately return the
|
|
// zone name as provided in parenthesis, but that seems locale-specific.
|
|
var match = /GMT[+-]\d+/.exec(new Date().toString());
|
|
value = (match && match[0]) || "GMT";
|
|
break;
|
|
case "javax.microedition.io.Connector.protocolpath":
|
|
value = "com.sun.midp.io";
|
|
break;
|
|
case "javax.microedition.io.Connector.protocolpath.fallback":
|
|
value = "com.sun.cldc.io";
|
|
break;
|
|
case "com.nokia.keyboard.type":
|
|
value = "None";
|
|
break;
|
|
case "com.nokia.mid.batterylevel":
|
|
// http://developer.nokia.com/community/wiki/Checking_battery_level_in_Java_ME
|
|
value = Math.floor(navigator.battery.level * 100).toString();
|
|
break;
|
|
case "com.nokia.mid.ui.version":
|
|
value = "1.7";
|
|
break;
|
|
case "com.nokia.mid.mnc":
|
|
if (mobileInfo.icc.mcc && mobileInfo.icc.mnc) {
|
|
// The concatenation of the MCC and MNC for the ICC (i.e. SIM card).
|
|
value = util.pad(mobileInfo.icc.mcc, 3) + util.pad(mobileInfo.icc.mnc, 3);
|
|
} else {
|
|
value = null;
|
|
}
|
|
break;
|
|
case "com.nokia.mid.networkID":
|
|
if (mobileInfo.network.mcc && mobileInfo.network.mnc) {
|
|
// The concatenation of MCC and MNC for the network.
|
|
value = util.pad(mobileInfo.network.mcc, 3) + util.pad(mobileInfo.network.mnc, 3);
|
|
} else {
|
|
value = null;
|
|
}
|
|
break;
|
|
case "com.nokia.mid.ui.customfontsize":
|
|
value = "true";
|
|
break;
|
|
case "classpathext":
|
|
value = null;
|
|
break;
|
|
case "supports.audio.capture":
|
|
value = "true";
|
|
break;
|
|
case "supports.video.capture":
|
|
value = "true";
|
|
break;
|
|
case "supports.recording":
|
|
value = "true";
|
|
break;
|
|
case "audio.encodings":
|
|
value = "encoding=audio/amr";
|
|
break;
|
|
case "video.snapshot.encodings":
|
|
// FIXME Some MIDlets pass a string that contains lots of constraints
|
|
// as the `imageType` which is not yet handled in DirectVideo.jpp, let's
|
|
// just put the whole string here as a workaround and fix this in issue #688.
|
|
value = "encoding=jpeg&quality=80&progressive=true&type=jfif&width=400&height=400";
|
|
break;
|
|
default:
|
|
if (MIDP.additionalProperties[key]) {
|
|
value = MIDP.additionalProperties[key];
|
|
} else if (typeof stubProperties[key] !== "undefined") {
|
|
value = stubProperties[key];
|
|
} else {
|
|
console.warn("UNKNOWN PROPERTY (java/lang/System): " + key);
|
|
stubProperties[key] = value = null;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return J2ME.newString(value);
|
|
};
|
|
|
|
Native["java/lang/System.currentTimeMillis.()J"] = function() {
|
|
return J2ME.returnLongValue(Date.now());
|
|
};
|
|
|
|
Native["com/sun/cldchi/jvm/JVM.unchecked_char_arraycopy.([CI[CII)V"] = function(src, srcOffset, dst, dstOffset, length) {
|
|
dst.set(src.subarray(srcOffset, srcOffset + length), dstOffset);
|
|
};
|
|
|
|
Native["com/sun/cldchi/jvm/JVM.unchecked_int_arraycopy.([II[III)V"] = function(src, srcOffset, dst, dstOffset, length) {
|
|
dst.set(src.subarray(srcOffset, srcOffset + length), dstOffset);
|
|
};
|
|
|
|
Native["com/sun/cldchi/jvm/JVM.unchecked_obj_arraycopy.([Ljava/lang/Object;I[Ljava/lang/Object;II)V"] = function(src, srcOffset, dst, dstOffset, length) {
|
|
if (dst !== src || dstOffset < srcOffset) {
|
|
for (var n = 0; n < length; ++n) {
|
|
dst[dstOffset++] = src[srcOffset++];
|
|
}
|
|
} else {
|
|
dstOffset += length;
|
|
srcOffset += length;
|
|
for (var n = 0; n < length; ++n)
|
|
dst[--dstOffset] = src[--srcOffset];
|
|
}
|
|
};
|
|
|
|
Native["com/sun/cldchi/jvm/JVM.monotonicTimeMillis.()J"] = function() {
|
|
return J2ME.returnLongValue(performance.now());
|
|
};
|
|
|
|
Native["java/lang/Object.getClass.()Ljava/lang/Class;"] = function() {
|
|
return $.getRuntimeKlass(this.klass).classObject;
|
|
};
|
|
|
|
Native["java/lang/Class.getSuperclass.()Ljava/lang/Class;"] = function() {
|
|
var superKlass = this.runtimeKlass.templateKlass.superKlass;
|
|
if (!superKlass) {
|
|
return null;
|
|
}
|
|
return superKlass.classInfo.getClassObject();
|
|
};
|
|
|
|
Native["java/lang/Class.invoke_clinit.()V"] = function() {
|
|
var classInfo = this.runtimeKlass.templateKlass.classInfo;
|
|
var className = classInfo.getClassNameSlow();
|
|
var clinit = classInfo.staticInitializer;
|
|
J2ME.preemptionLockLevel++;
|
|
if (clinit && clinit.classInfo.getClassNameSlow() === className) {
|
|
$.ctx.executeMethod(clinit);
|
|
}
|
|
};
|
|
|
|
Native["java/lang/Class.invoke_verify.()V"] = function() {
|
|
// There is currently no verification.
|
|
};
|
|
|
|
Native["java/lang/Class.init9.()V"] = function() {
|
|
$.setClassInitialized(this.runtimeKlass);
|
|
J2ME.preemptionLockLevel--;
|
|
};
|
|
|
|
Native["java/lang/Class.getName.()Ljava/lang/String;"] = function() {
|
|
return J2ME.newString(this.runtimeKlass.templateKlass.classInfo.getClassNameSlow().replace(/\//g, "."));
|
|
};
|
|
|
|
Native["java/lang/Class.forName0.(Ljava/lang/String;)V"] = function(name) {
|
|
var classInfo = null;
|
|
try {
|
|
if (!name)
|
|
throw new J2ME.ClassNotFoundException();
|
|
var className = J2ME.fromJavaString(name).replace(/\./g, "/");
|
|
classInfo = CLASSES.getClass(className);
|
|
} catch (e) {
|
|
if (e instanceof (J2ME.ClassNotFoundException))
|
|
throw $.newClassNotFoundException("'" + e.message + "' not found.");
|
|
throw e;
|
|
}
|
|
// The following can trigger an unwind.
|
|
J2ME.classInitCheck(classInfo);
|
|
};
|
|
|
|
Native["java/lang/Class.forName1.(Ljava/lang/String;)Ljava/lang/Class;"] = function(name) {
|
|
var className = J2ME.fromJavaString(name).replace(/\./g, "/");
|
|
var classInfo = CLASSES.getClass(className);
|
|
var classObject = classInfo.getClassObject();
|
|
return classObject;
|
|
};
|
|
|
|
Native["java/lang/Class.newInstance0.()Ljava/lang/Object;"] = function() {
|
|
if (this.runtimeKlass.templateKlass.classInfo.isInterface ||
|
|
this.runtimeKlass.templateKlass.classInfo.isAbstract) {
|
|
throw $.newInstantiationException("Can't instantiate interfaces or abstract classes");
|
|
}
|
|
|
|
if (this.runtimeKlass.templateKlass.classInfo instanceof J2ME.ArrayClassInfo) {
|
|
throw $.newInstantiationException("Can't instantiate array classes");
|
|
}
|
|
|
|
return new this.runtimeKlass.templateKlass;
|
|
};
|
|
|
|
Native["java/lang/Class.newInstance1.(Ljava/lang/Object;)V"] = function(o) {
|
|
// The following can trigger an unwind.
|
|
var methodInfo = o.klass.classInfo.getLocalMethodByNameString("<init>", "()V", false);
|
|
if (!methodInfo) {
|
|
throw $.newInstantiationException("Can't instantiate classes without a nullary constructor");
|
|
}
|
|
J2ME.getLinkedMethod(methodInfo).call(o);
|
|
};
|
|
|
|
Native["java/lang/Class.isInterface.()Z"] = function() {
|
|
return this.runtimeKlass.templateKlass.classInfo.isInterface ? 1 : 0;
|
|
};
|
|
|
|
Native["java/lang/Class.isArray.()Z"] = function() {
|
|
return this.runtimeKlass.templateKlass.classInfo instanceof J2ME.ArrayClassInfo ? 1 : 0;
|
|
};
|
|
|
|
Native["java/lang/Class.isAssignableFrom.(Ljava/lang/Class;)Z"] = function(fromClass) {
|
|
if (!fromClass)
|
|
throw $.newNullPointerException();
|
|
return J2ME.isAssignableTo(fromClass.runtimeKlass.templateKlass, this.runtimeKlass.templateKlass) ? 1 : 0;
|
|
};
|
|
|
|
Native["java/lang/Class.isInstance.(Ljava/lang/Object;)Z"] = function(obj) {
|
|
return obj && J2ME.isAssignableTo(obj.klass, this.runtimeKlass.templateKlass) ? 1 : 0;
|
|
};
|
|
|
|
Native["java/lang/Float.floatToIntBits.(F)I"] = function(f) {
|
|
return aliasedF32[0] = f, aliasedI32[0];
|
|
}
|
|
|
|
Native["java/lang/Float.intBitsToFloat.(I)F"] = function (i) {
|
|
return aliasedI32[0] = i, aliasedF32[0];
|
|
}
|
|
|
|
Native["java/lang/Double.doubleToLongBits.(D)J"] = function (d) {
|
|
aliasedF64[0] = d;
|
|
return J2ME.returnLong(aliasedI32[0], aliasedI32[1]);
|
|
}
|
|
|
|
Native["java/lang/Double.longBitsToDouble.(J)D"] = function (l, h) {
|
|
aliasedI32[0] = l;
|
|
aliasedI32[1] = h;
|
|
return aliasedF64[0];
|
|
}
|
|
|
|
Native["java/lang/Throwable.fillInStackTrace.()V"] = function() {
|
|
this.stackTrace = [];
|
|
J2ME.traceWriter && J2ME.traceWriter.writeLn("REDUX");
|
|
//$.ctx.frames.forEach(function(frame) {
|
|
// if (!frame.methodInfo)
|
|
// return;
|
|
// var methodInfo = frame.methodInfo;
|
|
// var methodName = methodInfo.name;
|
|
// if (!methodName)
|
|
// return;
|
|
// var classInfo = methodInfo.classInfo;
|
|
// var className = classInfo.getClassNameSlow();
|
|
// this.stackTrace.unshift({ className: className, methodName: methodName, methodSignature: methodInfo.signature, offset: frame.bci });
|
|
//}.bind(this));
|
|
};
|
|
|
|
Native["java/lang/Throwable.obtainBackTrace.()Ljava/lang/Object;"] = function() {
|
|
var resultAddr = 0;
|
|
// XXX: Untested.
|
|
if (this.stackTrace) {
|
|
var depth = this.stackTrace.length;
|
|
var classNamesAddr = J2ME.newStringArray(depth);
|
|
var classNames = J2ME.getArrayFromAddr(classNamesAddr);
|
|
var methodNamesAddr = J2ME.newStringArray(depth);
|
|
var methodNames = J2ME.getArrayFromAddr(methodNamesAddr);
|
|
var methodSignaturesAddr = J2ME.newStringArray(depth);
|
|
var methodSignatures = J2ME.getArrayFromAddr(methodSignaturesAddr);
|
|
var offsetsAddr = J2ME.newIntArray(depth);
|
|
var offsets = J2ME.getArrayFromAddr(offsetsAddr);
|
|
this.stackTrace.forEach(function(e, n) {
|
|
classNames[n] = J2ME.newString(e.className);
|
|
methodNames[n] = J2ME.newString(e.methodName);
|
|
methodSignatures[n] = J2ME.newString(e.methodSignature);
|
|
offsets[n] = e.offset;
|
|
});
|
|
resultAddr = J2ME.newObjectArray(3);
|
|
var result = J2ME.getArrayFromAddr(resultAddr);
|
|
result[0] = classNamesAddr;
|
|
result[1] = methodNamesAddr;
|
|
result[2] = methodSignaturesAddr;
|
|
result[3] = offsetsAddr;
|
|
}
|
|
return resultAddr;
|
|
};
|
|
|
|
Native["java/lang/Runtime.freeMemory.()J"] = function() {
|
|
return J2ME.returnLong(0x800000, 0);
|
|
};
|
|
|
|
Native["java/lang/Runtime.totalMemory.()J"] = function() {
|
|
return J2ME.returnLong(0x1000000, 0);
|
|
};
|
|
|
|
Native["java/lang/Runtime.gc.()V"] = function() {
|
|
};
|
|
|
|
Native["java/lang/Math.floor.(D)D"] = function(val) {
|
|
return Math.floor(val);
|
|
};
|
|
|
|
Native["java/lang/Math.asin.(D)D"] = function(val) {
|
|
return Math.asin(val);
|
|
};
|
|
|
|
Native["java/lang/Math.acos.(D)D"] = function(val) {
|
|
return Math.acos(val);
|
|
};
|
|
|
|
Native["java/lang/Math.atan.(D)D"] = function(val) {
|
|
return Math.atan(val);
|
|
};
|
|
|
|
Native["java/lang/Math.atan2.(DD)D"] = function(x, y) {
|
|
return Math.atan2(x, y);
|
|
};
|
|
|
|
Native["java/lang/Math.sin.(D)D"] = function(val) {
|
|
return Math.sin(val);
|
|
};
|
|
|
|
Native["java/lang/Math.cos.(D)D"] = function(val) {
|
|
return Math.cos(val);
|
|
};
|
|
|
|
Native["java/lang/Math.tan.(D)D"] = function(val) {
|
|
return Math.tan(val);
|
|
};
|
|
|
|
Native["java/lang/Math.sqrt.(D)D"] = function(val) {
|
|
return Math.sqrt(val);
|
|
};
|
|
|
|
Native["java/lang/Math.ceil.(D)D"] = function(val) {
|
|
return Math.ceil(val);
|
|
};
|
|
|
|
Native["java/lang/Math.floor.(D)D"] = function(val) {
|
|
return Math.floor(val);
|
|
};
|
|
|
|
Native["java/lang/Thread.currentThread.()Ljava/lang/Thread;"] = function() {
|
|
return getHandle($.ctx.threadAddress);
|
|
};
|
|
|
|
Native["java/lang/Thread.setPriority0.(II)V"] = function(oldPriority, newPriority) {
|
|
};
|
|
|
|
Native["java/lang/Thread.start0.()V"] = function() {
|
|
// The main thread starts during bootstrap and don't allow calling start()
|
|
// on already running threads.
|
|
if (this._address === $.ctx.runtime.mainThread || this.nativeAlive)
|
|
throw $.newIllegalThreadStateException();
|
|
this.nativeAlive = 1;
|
|
// XXX this.pid seems to be unused, so remove it.
|
|
this.pid = util.id();
|
|
// Create a context for the thread and start it.
|
|
var newCtx = new Context($.ctx.runtime);
|
|
newCtx.threadAddress = this._address;
|
|
|
|
var classInfo = CLASSES.getClass("org/mozilla/internal/Sys");
|
|
var run = classInfo.getMethodByNameString("runThread", "(Ljava/lang/Thread;)V", true);
|
|
newCtx.nativeThread.pushFrame(null);
|
|
newCtx.nativeThread.pushFrame(run);
|
|
newCtx.nativeThread.frame.setParameter(J2ME.Kind.Reference, 0, this);
|
|
newCtx.start();
|
|
}
|
|
|
|
Native["java/lang/Thread.activeCount.()I"] = function() {
|
|
return $.ctx.runtime.threadCount;
|
|
};
|
|
|
|
var consoleBuffer = "";
|
|
|
|
function flushConsoleBuffer() {
|
|
if (consoleBuffer.length) {
|
|
var temp = consoleBuffer;
|
|
consoleBuffer = "";
|
|
console.info(temp);
|
|
}
|
|
}
|
|
|
|
console.print = function(ch) {
|
|
if (ch === 10) {
|
|
flushConsoleBuffer();
|
|
} else {
|
|
consoleBuffer += String.fromCharCode(ch);
|
|
}
|
|
};
|
|
|
|
Native["com/sun/cldchi/io/ConsoleOutputStream.write.(I)V"] = function(ch) {
|
|
console.print(ch);
|
|
};
|
|
|
|
Native["com/sun/cldc/io/ResourceInputStream.open.(Ljava/lang/String;)Ljava/lang/Object;"] = function(name) {
|
|
var fileName = J2ME.fromJavaString(name);
|
|
var data = JARStore.loadFile(fileName);
|
|
var obj = null;
|
|
if (data) {
|
|
obj = J2ME.newObject(CLASSES.java_lang_Object.klass);
|
|
setNative(obj, {
|
|
data: data,
|
|
pos: 0,
|
|
});
|
|
}
|
|
return obj;
|
|
};
|
|
|
|
Native["com/sun/cldc/io/ResourceInputStream.clone.(Ljava/lang/Object;)Ljava/lang/Object;"] = function(source) {
|
|
var obj = J2ME.newObject(CLASSES.java_lang_Object.klass);
|
|
var sourceDecoder = getNative(source);
|
|
setNative(obj, {
|
|
data: new Uint8Array(sourceDecoder.data),
|
|
pos: sourceDecoder.pos,
|
|
});
|
|
return obj;
|
|
};
|
|
|
|
Native["com/sun/cldc/io/ResourceInputStream.bytesRemain.(Ljava/lang/Object;)I"] = function(fileDecoder) {
|
|
var handle = getNative(fileDecoder);
|
|
return handle.data.length - handle.pos;
|
|
};
|
|
|
|
Native["com/sun/cldc/io/ResourceInputStream.readByte.(Ljava/lang/Object;)I"] = function(fileDecoder) {
|
|
var handle = getNative(fileDecoder);
|
|
return (handle.data.length - handle.pos > 0) ? handle.data[handle.pos++] : -1;
|
|
};
|
|
|
|
Native["com/sun/cldc/io/ResourceInputStream.readBytes.(Ljava/lang/Object;[BII)I"] = function(fileDecoder, b, off, len) {
|
|
var handle = getNative(fileDecoder);
|
|
var data = handle.data;
|
|
var remaining = data.length - handle.pos;
|
|
if (len > remaining)
|
|
len = remaining;
|
|
for (var n = 0; n < len; ++n)
|
|
b[off+n] = data[handle.pos+n];
|
|
handle.pos += len;
|
|
return (len > 0) ? len : -1;
|
|
};
|
|
|
|
Native["java/lang/ref/WeakReference.initializeWeakReference.(Ljava/lang/Object;)V"] = function(target) {
|
|
// XXX Make these real weak references.
|
|
setNative(this, target);
|
|
};
|
|
|
|
Native["java/lang/ref/WeakReference.get.()Ljava/lang/Object;"] = function() {
|
|
var target = getNative(this);
|
|
return target ? target : null;
|
|
};
|
|
|
|
Native["java/lang/ref/WeakReference.clear.()V"] = function() {
|
|
deleteNative(this);
|
|
};
|
|
|
|
Native["com/sun/cldc/isolate/Isolate.registerNewIsolate.()V"] = function() {
|
|
this._id = util.id();
|
|
};
|
|
|
|
Native["com/sun/cldc/isolate/Isolate.getStatus.()I"] = function() {
|
|
var runtime = Runtime.isolateMap[this._address];
|
|
return runtime ? runtime.status : J2ME.RuntimeStatus.New;
|
|
};
|
|
|
|
Native["com/sun/cldc/isolate/Isolate.nativeStart.()V"] = function() {
|
|
$.ctx.runtime.jvm.startIsolate(this);
|
|
};
|
|
|
|
Native["com/sun/cldc/isolate/Isolate.waitStatus.(I)V"] = function(status) {
|
|
var runtime = Runtime.isolateMap[this._address];
|
|
asyncImpl("V", new Promise(function(resolve, reject) {
|
|
if (runtime.status >= status) {
|
|
resolve();
|
|
return;
|
|
}
|
|
function waitForStatus() {
|
|
if (runtime.status >= status) {
|
|
resolve();
|
|
return;
|
|
}
|
|
runtime.waitStatus(waitForStatus);
|
|
}
|
|
waitForStatus();
|
|
}));
|
|
};
|
|
|
|
Native["com/sun/cldc/isolate/Isolate.currentIsolate0.()Lcom/sun/cldc/isolate/Isolate;"] = function() {
|
|
return getHandle($.ctx.runtime.isolateAddress);
|
|
};
|
|
|
|
Native["com/sun/cldc/isolate/Isolate.getIsolates0.()[Lcom/sun/cldc/isolate/Isolate;"] = function() {
|
|
var isolatesAddr = J2ME.newObjectArray(Runtime.all.size);
|
|
var isolates = J2ME.getArrayFromAddr(isolatesAddr);
|
|
var n = 0;
|
|
Runtime.all.forEach(function(runtime) {
|
|
isolates[n++] = runtime.isolateAddress;
|
|
});
|
|
return isolatesAddr;
|
|
};
|
|
|
|
Native["com/sun/cldc/isolate/Isolate.setPriority0.(I)V"] = function(newPriority) {
|
|
// XXX Figure out if there's anything to do here. If not, say so.
|
|
};
|
|
|
|
Native["com/sun/j2me/content/AppProxy.midletIsAdded.(ILjava/lang/String;)V"] = function(suiteId, className) {
|
|
console.warn("com/sun/j2me/content/AppProxy.midletIsAdded.(ILjava/lang/String;)V not implemented");
|
|
};
|
|
|
|
Native["com/nokia/mid/impl/jms/core/Launcher.handleContent.(Ljava/lang/String;)V"] = function(content) {
|
|
var fileName = J2ME.fromJavaString(content);
|
|
|
|
var ext = fileName.split('.').pop().toLowerCase();
|
|
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#Supported_image_formats
|
|
if (["jpg", "jpeg", "gif", "apng", "png", "bmp", "ico"].indexOf(ext) == -1) {
|
|
console.error("File not supported: " + fileName);
|
|
throw $.newException("File not supported: " + fileName);
|
|
}
|
|
|
|
// `fileName` is supposed to be a full path, but we don't support
|
|
// partition, e.g. `C:` or `E:` etc, so the `fileName` we got here
|
|
// is something like: `Photos/sampleImage.jpg`, we need to prepend
|
|
// the root dir to make sure it's valid.
|
|
var imgData = fs.getBlob("/" + fileName);
|
|
if (!imgData) {
|
|
console.error("File not found: " + fileName);
|
|
throw $.newException("File not found: " + fileName);
|
|
}
|
|
|
|
var maskId = "image-launcher";
|
|
var mask = document.getElementById(maskId);
|
|
|
|
function _revokeImageURL() {
|
|
URL.revokeObjectURL(/url\((.+)\)/ig.exec(mask.style.backgroundImage)[1]);
|
|
}
|
|
|
|
if (mask) {
|
|
_revokeImageURL();
|
|
} else {
|
|
mask = document.createElement("div");
|
|
mask.id = maskId;
|
|
mask.onclick = mask.ontouchstart = function() {
|
|
_revokeImageURL();
|
|
mask.parentNode.removeChild(mask);
|
|
};
|
|
|
|
document.getElementById("main").appendChild(mask);
|
|
}
|
|
|
|
mask.style.backgroundImage = "url(" +
|
|
URL.createObjectURL(imgData) + ")";
|
|
};
|
|
|
|
function addUnimplementedNative(signature, returnValue) {
|
|
var doNotWarn;
|
|
|
|
if (typeof returnValue === "function") {
|
|
doNotWarn = returnValue;
|
|
} else {
|
|
doNotWarn = function() { return returnValue };
|
|
}
|
|
|
|
var warnOnce = function() {
|
|
console.warn(signature + " not implemented");
|
|
warnOnce = doNotWarn;
|
|
return doNotWarn();
|
|
};
|
|
|
|
Native[signature] = function() { return warnOnce() };
|
|
}
|
|
|
|
Native["org/mozilla/internal/Sys.eval.(Ljava/lang/String;)V"] = function(src) {
|
|
if (!release) {
|
|
eval(J2ME.fromJavaString(src));
|
|
}
|
|
};
|
|
|
|
Native["java/lang/String.intern.()Ljava/lang/String;"] = function() {
|
|
var value = J2ME.getArrayFromAddr(this.value);
|
|
var internedStrings = J2ME.internedStrings;
|
|
var internedString = internedStrings.getByRange(value, this.offset, this.count);
|
|
if (internedString !== null) {
|
|
return internedString;
|
|
}
|
|
internedStrings.put(value.subarray(this.offset, this.offset + this.count), this);
|
|
return this;
|
|
};
|
|
|
|
var profileStarted = false;
|
|
Native["org/mozilla/internal/Sys.startProfile.()V"] = function() {
|
|
if (profile === 4) {
|
|
if (!profileStarted) {
|
|
profileStarted = true;
|
|
|
|
console.log("Start profile at: " + performance.now());
|
|
startTimeline();
|
|
}
|
|
}
|
|
};
|
|
|
|
var profileSaved = false;
|
|
Native["org/mozilla/internal/Sys.stopProfile.()V"] = function() {
|
|
if (profile === 4) {
|
|
if (!profileSaved) {
|
|
profileSaved = true;
|
|
|
|
console.log("Stop profile at: " + performance.now());
|
|
setZeroTimeout(function() {
|
|
stopAndSaveTimeline();
|
|
});
|
|
}
|
|
}
|
|
};
|