зеркало из https://github.com/mozilla/pluotsorbet.git
274 строки
9.5 KiB
TypeScript
274 строки
9.5 KiB
TypeScript
/**
|
|
* Asm.js native module declaration, this is defined by lib/native.js
|
|
*/
|
|
declare var ASM;
|
|
|
|
var Native = Object.create(null);
|
|
|
|
/**
|
|
* Asm.js heap buffer and views.
|
|
*/
|
|
var buffer = ASM.buffer;
|
|
var i8: Int8Array = ASM.HEAP8;
|
|
var u8: Uint8Array = ASM.HEAPU8;
|
|
var i16: Int16Array = ASM.HEAP16;
|
|
var u16: Uint16Array = ASM.HEAPU16;
|
|
var i32: Int32Array = ASM.HEAP32;
|
|
var u32: Uint32Array = ASM.HEAPU32;
|
|
var f32: Float32Array = ASM.HEAPF32;
|
|
var f64: Float64Array = ASM.HEAPF64;
|
|
|
|
var aliasedI32 = J2ME.IntegerUtilities.i32;
|
|
var aliasedF32 = J2ME.IntegerUtilities.f32;
|
|
var aliasedF64 = J2ME.IntegerUtilities.f64;
|
|
|
|
module J2ME {
|
|
import assert = Debug.assert;
|
|
import Bytecodes = Bytecode.Bytecodes;
|
|
import toHEX = IntegerUtilities.toHEX;
|
|
|
|
export function asyncImplOld(returnKind: string, promise: Promise<any>, cleanup?: Function) {
|
|
return asyncImpl(kindCharacterToKind(returnKind), promise, cleanup);
|
|
}
|
|
|
|
/**
|
|
* Suspends the execution of the current thread and resumes it later once the specified
|
|
* |promise| is fulfilled.
|
|
*
|
|
* |onFulfilled| is called with one or two arguments |l| and |h|. |l| can be any
|
|
* value, while |h| can only ever be the high bits of a long value.
|
|
*
|
|
* |onRejected| is called with a java.lang.Exception object.
|
|
*/
|
|
export function asyncImpl(returnKind: Kind, promise: Promise<any>, cleanup?: Function) {
|
|
var ctx = $.ctx;
|
|
|
|
promise.then(function onFulfilled(l: any, h?: number) {
|
|
var thread = ctx.nativeThread;
|
|
thread.pushPendingNativeFrames();
|
|
|
|
// Push return value.
|
|
var sp = thread.sp;
|
|
switch (returnKind) {
|
|
case Kind.Double: // Doubles are passed in as a number value.
|
|
aliasedF64[0] = l;
|
|
i32[sp++] = aliasedI32[0];
|
|
i32[sp++] = aliasedI32[1];
|
|
break;
|
|
case Kind.Float:
|
|
f32[sp++] = l;
|
|
break;
|
|
case Kind.Long:
|
|
i32[sp++] = l;
|
|
i32[sp++] = h;
|
|
break;
|
|
case Kind.Int:
|
|
case Kind.Byte:
|
|
case Kind.Char:
|
|
case Kind.Short:
|
|
case Kind.Boolean:
|
|
i32[sp++] = l;
|
|
break;
|
|
case Kind.Reference:
|
|
release || assert(l !== "number", "async native return value is a number");
|
|
i32[sp++] = l;
|
|
break;
|
|
case Kind.Void:
|
|
break;
|
|
default:
|
|
release || J2ME.Debug.assert(false, "Invalid Kind: " + Kind[returnKind]);
|
|
}
|
|
thread.sp = sp;
|
|
|
|
cleanup && cleanup();
|
|
|
|
Scheduler.enqueue(ctx);
|
|
}, function onRejected(exception: java.lang.Exception) {
|
|
var thread = ctx.nativeThread;
|
|
thread.pushPendingNativeFrames();
|
|
var classInfo = CLASSES.getClass("org/mozilla/internal/Sys");
|
|
var methodInfo = classInfo.getMethodByNameString("throwException", "(Ljava/lang/Exception;)V");
|
|
|
|
thread.pushMarkerFrame(FrameType.Interrupt);
|
|
thread.pushFrame(methodInfo);
|
|
thread.frame.setParameter(J2ME.Kind.Reference, 0, exception._address);
|
|
|
|
cleanup && cleanup();
|
|
|
|
Scheduler.enqueue(ctx);
|
|
});
|
|
|
|
$.pause("Async");
|
|
$.nativeBailout(returnKind);
|
|
}
|
|
|
|
Native["java/lang/Thread.sleep.(J)V"] = function(addr: number, delayL: number, delayH: number) {
|
|
asyncImpl(Kind.Void, new Promise(function(resolve, reject) {
|
|
window.setTimeout(resolve, longToNumber(delayL, delayH));
|
|
}));
|
|
};
|
|
|
|
Native["java/lang/Thread.isAlive.()Z"] = function(addr: number) {
|
|
var self = <java.lang.Thread>getHandle(addr);
|
|
return self.nativeAlive ? 1 : 0;
|
|
};
|
|
|
|
Native["java/lang/Thread.yield.()V"] = function(addr: number) {
|
|
$.yield("Thread.yield");
|
|
$.nativeBailout(Kind.Void);
|
|
};
|
|
|
|
Native["java/lang/Object.wait.(J)V"] = function(addr: number, timeoutL: number, timeoutH: number) {
|
|
$.ctx.wait(addr, longToNumber(timeoutL, timeoutH));
|
|
if (U) {
|
|
$.nativeBailout(Kind.Void);
|
|
}
|
|
};
|
|
|
|
Native["java/lang/Object.notify.()V"] = function(addr: number) {
|
|
$.ctx.notify(addr, false);
|
|
// TODO Remove this assertion after investigating why wakeup on another ctx can unwind see comment in Context.notify..
|
|
release || assert(!U, "Unexpected unwind in java/lang/Object.notify.()V.");
|
|
};
|
|
|
|
Native["java/lang/Object.notifyAll.()V"] = function(addr: number) {
|
|
$.ctx.notify(addr, true);
|
|
// TODO Remove this assertion after investigating why wakeup on another ctx can unwind see comment in Context.notify.
|
|
release || assert(!U, "Unexpected unwind in java/lang/Object.notifyAll.()V.");
|
|
};
|
|
|
|
Native["java/lang/ref/WeakReference.initializeWeakReference.(Ljava/lang/Object;)V"] = function(addr: number, targetAddr: number): void {
|
|
if (targetAddr === J2ME.Constants.NULL) {
|
|
return;
|
|
}
|
|
|
|
var weakRef = (<java.lang.ref.WeakReference>getHandle(addr));
|
|
weakRef.holder = gcMallocAtomic(4);
|
|
i32[weakRef.holder >> 2] = targetAddr;
|
|
ASM._gcRegisterDisappearingLink(weakRef.holder, targetAddr);
|
|
};
|
|
|
|
Native["java/lang/ref/WeakReference.get.()Ljava/lang/Object;"] = function(addr: number): number {
|
|
var weakRef = (<java.lang.ref.WeakReference>getHandle(addr));
|
|
if (weakRef.holder === J2ME.Constants.NULL) {
|
|
return J2ME.Constants.NULL;
|
|
}
|
|
return i32[weakRef.holder >> 2];
|
|
};
|
|
|
|
Native["java/lang/ref/WeakReference.clear.()V"] = function(addr: number): void {
|
|
var weakRef = (<java.lang.ref.WeakReference>getHandle(addr));
|
|
ASM._gcUnregisterDisappearingLink(weakRef.holder);
|
|
weakRef.holder = J2ME.Constants.NULL;
|
|
};
|
|
|
|
Native["org/mozilla/internal/Sys.getUnwindCount.()I"] = function(addr: number) {
|
|
return unwindCount;
|
|
};
|
|
|
|
Native["org/mozilla/internal/Sys.constructCurrentThread.()V"] = function(addr: number) {
|
|
var methodInfo = CLASSES.java_lang_Thread.getMethodByNameString("<init>", "(Ljava/lang/String;)V");
|
|
getLinkedMethod(methodInfo)($.mainThread, J2ME.newString("main"));
|
|
if (U) {
|
|
$.nativeBailout(J2ME.Kind.Void, J2ME.Bytecode.Bytecodes.INVOKESPECIAL);
|
|
}
|
|
|
|
// We've already set this in JVM.createIsolateCtx, but calling the instance
|
|
// initializer above resets it, so we set it again here.
|
|
//
|
|
// We used to store this state on the persistent native object, which was
|
|
// unaffected by the instance initializer; but now we store it on the Java
|
|
// object, which is susceptible to it, since there is no persistent native
|
|
// object anymore).
|
|
//
|
|
// XXX Figure out a less hacky approach.
|
|
//
|
|
var thread = <java.lang.Thread>getHandle($.mainThread);
|
|
thread.nativeAlive = true;
|
|
};
|
|
|
|
Native["org/mozilla/internal/Sys.getIsolateMain.()Ljava/lang/String;"] = function(addr: number): number {
|
|
var isolate = <com.sun.cldc.isolate.Isolate>getHandle($.isolateAddress);
|
|
return isolate._mainClass;
|
|
};
|
|
|
|
Native["org/mozilla/internal/Sys.executeMain.(Ljava/lang/Class;)V"] = function(addr: number, mainAddr: number) {
|
|
var main = <java.lang.Class>getHandle(mainAddr);
|
|
var entryPoint = CLASSES.getEntryPoint(J2ME.classIdToClassInfoMap[main.vmClass]);
|
|
if (!entryPoint)
|
|
throw new Error("Could not find isolate main.");
|
|
|
|
var isolate = <com.sun.cldc.isolate.Isolate>getHandle($.isolateAddress);
|
|
|
|
getLinkedMethod(entryPoint)(Constants.NULL, isolate._mainArgs);
|
|
if (U) {
|
|
$.nativeBailout(J2ME.Kind.Void, J2ME.Bytecode.Bytecodes.INVOKESTATIC);
|
|
}
|
|
};
|
|
|
|
Native["java/lang/Throwable.fillInStackTrace.()V"] = function(addr: number) {
|
|
var frame = $.ctx.nativeThread.frame;
|
|
var tp = $.ctx.nativeThread.tp;
|
|
var fp = frame.fp;
|
|
var sp = frame.sp;
|
|
var pc = frame.pc;
|
|
var stackTrace = [];
|
|
setNative(addr, stackTrace);
|
|
while (true) {
|
|
release || assert(fp >= (tp >> 2), "Invalid frame pointer.");
|
|
if (frame.fp === (tp >> 2)) {
|
|
break;
|
|
}
|
|
stackTrace.push({
|
|
frameType: frame.type,
|
|
methodInfo: frame.methodInfo,
|
|
pc: frame.pc
|
|
});
|
|
|
|
frame.set(frame.thread, i32[frame.fp + FrameLayout.CallerFPOffset],
|
|
frame.fp + frame.parameterOffset,
|
|
i32[frame.fp + FrameLayout.CallerRAOffset]);
|
|
}
|
|
frame.fp = fp;
|
|
frame.sp = sp;
|
|
frame.pc = pc;
|
|
};
|
|
|
|
Native["java/lang/Throwable.obtainBackTrace.()Ljava/lang/Object;"] = function(addr: number): number {
|
|
var resultAddr = J2ME.Constants.NULL;
|
|
var stackTrace = <[any]>NativeMap.get(addr);
|
|
if (stackTrace) {
|
|
var depth = 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);
|
|
stackTrace.forEach(function(e, n) {
|
|
if (e.frameType === FrameType.Interpreter) {
|
|
var methodInfo = <MethodInfo>e.methodInfo;
|
|
classNames[n] = J2ME.newString(methodInfo.classInfo.getClassNameSlow());
|
|
methodNames[n] = J2ME.newString(methodInfo.name);
|
|
methodSignatures[n] = J2ME.newString(methodInfo.signature);
|
|
offsets[n] = e.pc;
|
|
} else {
|
|
classNames[n] = J2ME.newString("MARKER FRAME " + FrameType[e.frameType]);
|
|
methodNames[n] = J2ME.newString("");
|
|
methodSignatures[n] = J2ME.newString("");
|
|
offsets[n] = e.pc;
|
|
}
|
|
});
|
|
resultAddr = J2ME.newObjectArray(4);
|
|
var result = J2ME.getArrayFromAddr(resultAddr);
|
|
result[0] = classNamesAddr;
|
|
result[1] = methodNamesAddr;
|
|
result[2] = methodSignaturesAddr;
|
|
result[3] = offsetsAddr;
|
|
}
|
|
return resultAddr;
|
|
};
|
|
}
|