зеркало из https://github.com/mozilla/pluotsorbet.git
better locking for class intialization
This commit is contained in:
Родитель
774d8d3d06
Коммит
d394243a8a
118
context.js
118
context.js
|
@ -7,6 +7,14 @@ function Context() {
|
|||
this.frames = [];
|
||||
}
|
||||
|
||||
var Context = (function() {
|
||||
var pid = 0;
|
||||
return function() {
|
||||
this.frames = [];
|
||||
this.pid = pid++;
|
||||
}
|
||||
})();
|
||||
|
||||
Context.prototype.current = function() {
|
||||
var frames = this.frames;
|
||||
return frames[frames.length - 1];
|
||||
|
@ -29,46 +37,45 @@ Context.prototype.popFrame = function() {
|
|||
return caller;
|
||||
}
|
||||
|
||||
Context.prototype.monitorEnter = function(obj) {
|
||||
var lock = obj.lock;
|
||||
if (!lock) {
|
||||
obj.lock = { thread: this.thread, count: 1, waiters: [] };
|
||||
return;
|
||||
}
|
||||
if (lock.thread === this.thread) {
|
||||
++lock.count;
|
||||
return;
|
||||
}
|
||||
lock.waiters.push(this);
|
||||
throw VM.Pause;
|
||||
}
|
||||
|
||||
Context.prototype.monitorLeave = function(obj) {
|
||||
var lock = obj.lock;
|
||||
if (lock.thread !== this.thread) {
|
||||
console.log("WARNING: thread tried to unlock a monitor it didn't own");
|
||||
return;
|
||||
}
|
||||
if (--lock.count > 0) {
|
||||
return;
|
||||
}
|
||||
var waiters = lock.waiters;
|
||||
obj.lock = null;
|
||||
for (var n = 0; n < waiters.length; ++n)
|
||||
window.setZeroTimeout(VM.execute.bind(null, waiters[n]));
|
||||
}
|
||||
|
||||
Context.prototype.pushClassInitFrame = function(classInfo) {
|
||||
console.log("pushClassInitFrame", classInfo.className, classInfo.initialized);
|
||||
if (classInfo.initialized)
|
||||
return;
|
||||
if (classInfo.superClass)
|
||||
this.pushClassInitFrame(classInfo.superClass);
|
||||
classInfo.initialized = true;
|
||||
var clinit = CLASSES.getMethod(classInfo, "<clinit>", "()V", true, false);
|
||||
if (!clinit)
|
||||
if (!clinit) {
|
||||
classInfo.initialized = true;
|
||||
return;
|
||||
this.pushFrame(clinit, 0);
|
||||
}
|
||||
// console.log("starting initialization of", classInfo.className, this.thread.toString());
|
||||
classInfo.thread = this.thread;
|
||||
var syntheticMethod = {
|
||||
classInfo: {
|
||||
constant_pool: [
|
||||
null,
|
||||
{ class_index: 2, name_and_type_index: 4 },
|
||||
{ name_index: 3 },
|
||||
{ bytes: "java/lang/Class" },
|
||||
{ name_index: 5, signature_index: 6 },
|
||||
{ bytes: "invoke_clinit" },
|
||||
{ bytes: "()V" },
|
||||
{ class_index: 2, name_and_type_index: 8 },
|
||||
{ name_index: 9, signature_index: 10 },
|
||||
{ bytes: "init9" },
|
||||
{ bytes: "()V" },
|
||||
],
|
||||
},
|
||||
code: [
|
||||
0x2a, // aload_0
|
||||
0x59, // dup
|
||||
0xb7, 0x00, 0x01, // invokespecial <idx=1>
|
||||
0xb7, 0x00, 0x07, // invokespecial <idx=7>
|
||||
0xb1, // return
|
||||
],
|
||||
exception_table: [],
|
||||
};
|
||||
this.current().stack.push(classInfo.getClassObject());
|
||||
this.pushFrame(syntheticMethod, 1);
|
||||
}
|
||||
|
||||
Context.prototype.backTrace = function() {
|
||||
|
@ -192,3 +199,48 @@ Context.prototype.start = function(stopFrame) {
|
|||
Context.prototype.resume = function() {
|
||||
this.start(this.stopFrame);
|
||||
}
|
||||
|
||||
Context.prototype.wait = function(obj) {
|
||||
if (!obj.waiters)
|
||||
obj.waiters = [];
|
||||
obj.waiters.push(this);
|
||||
throw VM.Pause;
|
||||
}
|
||||
|
||||
Context.prototype.notify = function(obj) {
|
||||
if (!obj.waiters || !obj.waiters.length)
|
||||
return;
|
||||
var waiter = obj.waiters.pop();
|
||||
window.setZeroTimeout(VM.execute.bind(null, waiter));
|
||||
}
|
||||
|
||||
Context.prototype.notifyAll = function(obj) {
|
||||
while (obj.waiters && obj.waiters.length)
|
||||
this.notify(obj);
|
||||
}
|
||||
|
||||
Context.prototype.monitorEnter = function(obj) {
|
||||
var lock = obj.lock;
|
||||
if (!lock) {
|
||||
obj.lock = { thread: this.thread, count: 1 };
|
||||
return;
|
||||
}
|
||||
if (lock.thread === this.thread) {
|
||||
++lock.count;
|
||||
return;
|
||||
}
|
||||
this.wait(obj);
|
||||
}
|
||||
|
||||
Context.prototype.monitorExit = function(obj) {
|
||||
var lock = obj.lock;
|
||||
if (lock.thread !== this.thread) {
|
||||
console.log("WARNING: thread tried to unlock a monitor it didn't own");
|
||||
return;
|
||||
}
|
||||
if (--lock.count > 0) {
|
||||
return;
|
||||
}
|
||||
obj.lock = null;
|
||||
this.notifyAll(obj);
|
||||
}
|
||||
|
|
5
jvm.js
5
jvm.js
|
@ -44,15 +44,14 @@ JVM.prototype.run = function(className) {
|
|||
CLASSES.java_lang_Object = CLASSES.loadClass("java/lang/Object");
|
||||
CLASSES.java_lang_Class = CLASSES.loadClass("java/lang/Class");
|
||||
CLASSES.java_lang_String = CLASSES.loadClass("java/lang/String");
|
||||
|
||||
ctx.pushClassInitFrame(CLASSES.java_lang_Thread = CLASSES.loadClass("java/lang/Thread"));
|
||||
ctx.execute(caller);
|
||||
CLASSES.java_lang_Thread = CLASSES.loadClass("java/lang/Thread");
|
||||
|
||||
ctx.thread = CLASSES.mainThread = CLASSES.newObject(CLASSES.java_lang_Thread);
|
||||
caller.stack.push(CLASSES.mainThread);
|
||||
caller.stack.push(ctx.newString("main"));
|
||||
ctx.pushFrame(CLASSES.getMethod(CLASSES.java_lang_Thread, "<init>", "(Ljava/lang/String;)V"), 2);
|
||||
ctx.execute(caller);
|
||||
|
||||
caller.stack.push(CLASSES.newArray("[Ljava/lang/String;", 0));
|
||||
ctx.pushFrame(entryPoint, 1);
|
||||
ctx.start(caller);
|
||||
|
|
4
main.js
4
main.js
|
@ -33,8 +33,8 @@ console.log = function() {
|
|||
|
||||
//runTest("TestThread");
|
||||
//runTest("TestRuntime");
|
||||
runTest("Andreas");
|
||||
//runTest("Andreas");
|
||||
//runTest("TestDate");
|
||||
//runTest("RunAll");
|
||||
runTest("RunAll");
|
||||
//runTest("TestArrays");
|
||||
//runTest("TestByteArrayOutputStream");
|
||||
|
|
21
native.js
21
native.js
|
@ -72,10 +72,25 @@ Native["java/lang/Object.getClass.()Ljava/lang/Class;"] = function(ctx, stack) {
|
|||
stack.push(stack.pop().class.getClassObject());
|
||||
}
|
||||
|
||||
Native["java/lang/Class.invoke_clinit.()V"] = function(ctx, stack) {
|
||||
var classInfo = stack.pop().vmClass;
|
||||
var clinit = CLASSES.getMethod(classInfo, "<clinit>", "()V", true);
|
||||
ctx.pushFrame(clinit, 0);
|
||||
throw VM.Yield;
|
||||
}
|
||||
|
||||
Native["java/lang/Class.init9.()V"] = function(ctx, stack) {
|
||||
var classObject = stack.pop();
|
||||
var classInfo = classObject.vmClass;
|
||||
classInfo.initialized = true;
|
||||
classInfo.thread = null;
|
||||
ctx.notifyAll(classObject);
|
||||
}
|
||||
|
||||
Native["java/lang/Class.getName.()Ljava/lang/String;"] = function(ctx, stack) {
|
||||
var obj = stack.pop();
|
||||
stack.push(util.cache(obj, "getName", function () {
|
||||
return ctx.newString(obj.vmClass.className.replace("/", ".", "g"));
|
||||
var classObject = stack.pop();
|
||||
stack.push(util.cache(classObject, "getName", function () {
|
||||
return ctx.newString(classObject.vmClass.className.replace("/", ".", "g"));
|
||||
}));
|
||||
}
|
||||
|
||||
|
|
30
vm.js
30
vm.js
|
@ -15,24 +15,21 @@ VM.execute = function(ctx) {
|
|||
var stack = frame.stack;
|
||||
|
||||
function pushFrame(methodInfo, consumes) {
|
||||
var lockObject;
|
||||
if (ACCESS_FLAGS.isSynchronized(methodInfo.access_flags)) {
|
||||
lockObject = ACCESS_FLAGS.isStatic(methodInfo.access_flags)
|
||||
? methodInfo.classInfo.getClassObject()
|
||||
: stack[stack.length - consumes];
|
||||
ctx.monitorEnter(lockObject);
|
||||
}
|
||||
frame = ctx.pushFrame(methodInfo, consumes);
|
||||
stack = frame.stack;
|
||||
cp = frame.cp;
|
||||
if (lockObject)
|
||||
frame.lockObject = lockObject;
|
||||
if (ACCESS_FLAGS.isSynchronized(methodInfo.access_flags)) {
|
||||
frame.lockObject = ACCESS_FLAGS.isStatic(methodInfo.access_flags)
|
||||
? methodInfo.classInfo.getClassObject()
|
||||
: frame.getLocal(0);
|
||||
ctx.monitorEnter(frame.lockObject);
|
||||
}
|
||||
return frame;
|
||||
}
|
||||
|
||||
function popFrame(consumes) {
|
||||
if (frame.lockObject)
|
||||
ctx.monitorLeave(frame.lockObject);
|
||||
ctx.monitorExit(frame.lockObject);
|
||||
var callee = frame;
|
||||
frame = ctx.popFrame();
|
||||
stack = frame.stack;
|
||||
|
@ -98,6 +95,13 @@ VM.execute = function(ctx) {
|
|||
function classInitCheck(classInfo, ip) {
|
||||
if (classInfo.initialized)
|
||||
return;
|
||||
if (classInfo.thread) {
|
||||
// Nothing to do if class initialization is currently in progress on this thread.
|
||||
if (classInfo.thread === ctx.thread)
|
||||
return;
|
||||
ctx.wait(classInfo.getClassObject());
|
||||
// not reached
|
||||
}
|
||||
frame.ip = ip;
|
||||
ctx.pushClassInitFrame(classInfo);
|
||||
throw VM.Yield;
|
||||
|
@ -105,7 +109,7 @@ VM.execute = function(ctx) {
|
|||
|
||||
while (true) {
|
||||
var op = frame.read8();
|
||||
console.log(frame.methodInfo.classInfo.className + " " + frame.methodInfo.name + " " + (frame.ip - 1) + " " + OPCODES[op] + " " + stack.join(","));
|
||||
// console.log("PID" + ctx.pid, frame.methodInfo.classInfo.className + " " + frame.methodInfo.name + " " + (frame.ip - 1) + " " + OPCODES[op] + " " + stack.join(","));
|
||||
switch (op) {
|
||||
case 0x00: // nop
|
||||
break;
|
||||
|
@ -165,7 +169,6 @@ VM.execute = function(ctx) {
|
|||
stack.push(constant.float);
|
||||
break;
|
||||
case TAGS.CONSTANT_String:
|
||||
// console.log(cp[constant.string_index].bytes);
|
||||
stack.push(ctx.newString(cp[constant.string_index].bytes));
|
||||
break;
|
||||
default:
|
||||
|
@ -920,7 +923,7 @@ VM.execute = function(ctx) {
|
|||
ctx.raiseException("java/lang/NullPointerException");
|
||||
break;
|
||||
}
|
||||
ctx.monitorLeave(obj);
|
||||
ctx.monitorExit(obj);
|
||||
break;
|
||||
case 0xc4: // wide
|
||||
break;
|
||||
|
@ -942,7 +945,6 @@ VM.execute = function(ctx) {
|
|||
var methodInfo = CLASSES.getMethod(classInfo, methodName, signature, isStatic);
|
||||
var consumes = Signature.parse(methodInfo.signature).IN.slots;
|
||||
if (isStatic) {
|
||||
console.log("invokestatic initialized=" + classInfo.initialized + " " + classInfo.className);
|
||||
classInitCheck(classInfo, startip);
|
||||
} else {
|
||||
++consumes;
|
||||
|
|
Загрузка…
Ссылка в новой задаче