lvm.js

changeset 73
6b43d68abc94
parent 72
dc73a60b3c06
child 74
a4a6adee1459
equal deleted inserted replaced
72:dc73a60b3c06 73:6b43d68abc94
23 var OP_FORPREP = 32; 23 var OP_FORPREP = 32;
24 var OP_CLOSURE = 36; 24 var OP_CLOSURE = 36;
25 25
26 var debugMode = false; 26 var debugMode = false;
27 27
28 function LValue(type, value) 28 function LValue(vm, type, value)
29 { 29 {
30 this.vm = vm;
30 this.type = type||"nil"; 31 this.type = type||"nil";
31 this.value = value; 32 this.value = value;
32 } 33 }
33 34
34 LValue.prototype = { 35 LValue.prototype = {
57 var val; 58 var val;
58 if(key.value in this.value) 59 if(key.value in this.value)
59 return this.value[key.value]; 60 return this.value[key.value];
60 else if(raw != true && this.metatable && this.metatable.type != "nil") 61 else if(raw != true && this.metatable && this.metatable.type != "nil")
61 { 62 {
62 var __index = this.metatable.index(new LValue("string", "__index")); 63 var __index = this.metatable.index(this.vm.LValue("__index"));
63 if(__index.type == "function") 64 if(__index.type == "function")
64 { 65 {
65 return LValueFromValue(__index.call([this, key])[0]); 66 return this.vm.LValue(__index.call([this, key])[0]);
66 } 67 }
67 else if(__index.type != "nil") 68 else if(__index.type != "nil")
68 return __index.index(key); 69 return __index.index(key);
69 } 70 }
70 return new LValue("nil", null); 71 return this.vm.LValue(null);
71 } 72 }
72 else 73 else
73 throw "Attempt to index a " + this.type + " value"; 74 throw "Attempt to index a " + this.type + " value";
74 }, 75 },
75 setIndex: function (key, value) 76 setIndex: function (key, value)
101 } 102 }
102 }, 103 },
103 add: function (op2) 104 add: function (op2)
104 { 105 {
105 var metamethod; 106 var metamethod;
106 var __add = LValueFromString("__add"); 107 var __add = this.vm.LValue("__add");
107 if(this.metatable) 108 if(this.metatable)
108 metamethod = this.metatable.index(__add); 109 metamethod = this.metatable.index(__add);
109 if((!metamethod || metamethod.type == "nil") && op2.metatable) 110 if((!metamethod || metamethod.type == "nil") && op2.metatable)
110 metamethod = op2.metatable.index(__add); 111 metamethod = op2.metatable.index(__add);
111 if(metamethod && metamethod.type != "nil") 112 if(metamethod && metamethod.type != "nil")
120 else 121 else
121 throw "Attempt to perform arithmetic on a "+this.type+" and "+op2.type; 122 throw "Attempt to perform arithmetic on a "+this.type+" and "+op2.type;
122 } 123 }
123 }; 124 };
124 125
125 function LValueFromString(string) 126 function LBinaryChunk(vm, chunk, start)
126 {
127 return new LValue("string", string);
128 }
129
130 function LValueFromFunction(vm, func)
131 {
132 var val = new LValue("function", func);
133 val.vm = vm;
134 return val;
135 }
136
137 function LValueFromValue(value)
138 {
139 switch(typeof(value))
140 {
141 case "number":
142 return new LValue("number", value);
143 case "string":
144 return new LValueFromString(value);
145 case "function":
146 return new LValueFromFunction(value);
147 case "object":
148 if(value == null)
149 return new LValue("nil", null);
150 else
151 return new LValue("table", value);
152 case "undefined":
153 return new LValue("nil", null);
154 default:
155 sys.puts( "Not able to convert type " +
156 typeof(value)+" from Javascript to Lua: "+sys.inspect(value));
157 throw "Not able to convert type " +
158 typeof(value)+" from Javascript to Lua";
159 }
160 }
161
162 function LBinaryChunk(chunk, start)
163 { 127 {
164 this.chunk = chunk; 128 this.chunk = chunk;
165 this.pos = start||12; 129 this.pos = start||12;
166 130
167 this.sourceName = this.readString(); 131 this.sourceName = this.readString();
198 { 162 {
199 var type = this.readByte(); 163 var type = this.readByte();
200 switch(type) 164 switch(type)
201 { 165 {
202 case 0: // Nil 166 case 0: // Nil
203 this.constants.push(new LValue("nil", null)); 167 this.constants.push(new LValue(vm, "nil", null));
204 break; 168 break;
205 case 1: // Boolean 169 case 1: // Boolean
206 this.constants.push(new LValue("boolean", this.readByte())); 170 this.constants.push(new LValue(vm, "boolean", this.readByte())); // FIXME type
207 break; 171 break;
208 case 3: // Number 172 case 3: // Number
209 this.constants.push(new LValue("number", this.readNumber())); 173 this.constants.push(new LValue(vm, "number", this.readNumber()));
210 break; 174 break;
211 case 4: // String 175 case 4: // String
212 this.constants.push(LValueFromString(this.readString())); 176 this.constants.push(new LValue(vm, "string", this.readString()));
213 break; 177 break;
214 default: 178 default:
215 throw "Invalid constant type "+type+" in bytecode"; 179 throw "Invalid constant type "+type+" in bytecode";
216 } 180 }
217 } 181 }
219 this.prototypes = []; 183 this.prototypes = [];
220 184
221 this.numPrototypes = this.readInt(); 185 this.numPrototypes = this.readInt();
222 for(var i=0;i<this.numPrototypes;i++) 186 for(var i=0;i<this.numPrototypes;i++)
223 { 187 {
224 var p = new LBinaryChunk(chunk, this.pos); 188 var p = new LBinaryChunk(vm, chunk, this.pos);
225 this.pos = p.pos; 189 this.pos = p.pos;
226 this.prototypes.push(p); 190 this.prototypes.push(p);
227 } 191 }
228 192
229 this.sourceLines = []; 193 this.sourceLines = [];
350 this.stack = []; 314 this.stack = [];
351 return this; 315 return this;
352 } 316 }
353 317
354 LVM.prototype = { 318 LVM.prototype = {
355 call: function (lfFunction) 319 LValue: function (value)
356 { 320 {
357 var frame = {f:lfFunction,pc:0,reg:[],entry:true}; 321 switch(typeof(value))
358 this.callstack.push(frame); 322 {
359 for(var i=0;i<lfFunction.maxStackSize;i++) 323 case "number":
360 frame.reg[i] = new LValue("nil", null); 324 return new LValue(this, "number", value);
361 return this.run(frame); 325 case "string":
326 return new LValue(this, "string", value);
327 case "function":
328 return new LValue(this, "function", value);
329 case "object":
330 if(value == null)
331 return new LValue(this, "nil", value);
332 else
333 return new LValue(this, "table", value);
334 case "undefined":
335 return new LValue(this, "nil", null);
336 default:
337 throw "Not able to convert type " +
338 typeof(value)+" from Javascript to Lua";
339 }
340 },
341 call: function (lfFunction, args)
342 {
343 if(typeof(lfFunction) == "function")
344 {
345 return lfFunction.apply(this, args);
346 }
347 else
348 {
349 var frame = {f:lfFunction,pc:0,entry:true};
350 if(args)
351 frame.reg = args.slice(0);
352 else
353 frame.reg = [];
354 this.callstack.push(frame);
355 for(var i=0;i<lfFunction.maxStackSize;i++)
356 frame.reg[i] = this.LValue(null);
357 return this.run(frame);
358 }
362 }, 359 },
363 run: function(frame) 360 run: function(frame)
364 { 361 {
365 var instruction; 362 var instruction;
366 while(this.callstack.length>0) 363 while(this.callstack.length>0)
376 case OP_MOVE: 373 case OP_MOVE:
377 frame.reg[INS_A(instruction)] = frame.reg[INS_B(instruction)]; 374 frame.reg[INS_A(instruction)] = frame.reg[INS_B(instruction)];
378 break; 375 break;
379 case OP_LOADNIL: 376 case OP_LOADNIL:
380 for(var i = INS_A(instruction);i<=INS_B(instruction);i++) 377 for(var i = INS_A(instruction);i<=INS_B(instruction);i++)
381 frame.reg[i] = new LValue("nil", null); 378 frame.reg[i] = new LValue(this, "nil", null);
382 break; 379 break;
383 case OP_LOADBOOL: 380 case OP_LOADBOOL:
384 frame.reg[INS_A(instruction)] = new LValue("boolean", INS_B(instruction)!=0); 381 frame.reg[INS_A(instruction)] = new LValue(this, "boolean", INS_B(instruction)!=0);
385 if(INS_C(instruction)!=0) 382 if(INS_C(instruction)!=0)
386 frame.pc++; 383 frame.pc++;
387 break; 384 break;
388 case OP_GETUPVAL: 385 case OP_GETUPVAL:
389 frame.reg[INS_A(instruction)] = frame.f.upvalues[INS_B(instruction)]; 386 frame.reg[INS_A(instruction)] = frame.f.upvalues[INS_B(instruction)];
402 var name = frame.f.constants[INS_Bx(instruction)]; 399 var name = frame.f.constants[INS_Bx(instruction)];
403 frame.f.environment.setIndex(name, frame.reg[instruction[1]]); 400 frame.f.environment.setIndex(name, frame.reg[instruction[1]]);
404 break; 401 break;
405 case OP_LOADK: 402 case OP_LOADK:
406 var constant = frame.f.constants[INS_Bx(instruction)]; 403 var constant = frame.f.constants[INS_Bx(instruction)];
407 frame.reg[INS_A(instruction)] = new LValue(constant.type, constant.value); 404 frame.reg[INS_A(instruction)] = new LValue(this, constant.type, constant.value);
408 break; 405 break;
409 case OP_NEWTABLE: 406 case OP_NEWTABLE:
410 frame.reg[INS_A(instruction)] = new LValue("table", {}); 407 frame.reg[INS_A(instruction)] = new LValue(this, "table", {});
411 break; 408 break;
412 case OP_GETTABLE: 409 case OP_GETTABLE:
413 var C = INS_C(instruction); 410 var C = INS_C(instruction);
414 var keysource = (C&0x100)?frame.f.constants:frame.reg; 411 var keysource = (C&0x100)?frame.f.constants:frame.reg;
415 var key = keysource[C&0xff]; 412 var key = keysource[C&0xff];
416 var value = frame.reg[INS_B(instruction)].index(key).value; 413 var value = frame.reg[INS_B(instruction)].index(key).value;
417 frame.reg[INS_A(instruction)] = new LValueFromValue(value); 414 frame.reg[INS_A(instruction)] = this.LValue(value);
418 break; 415 break;
419 case OP_SETTABLE: 416 case OP_SETTABLE:
420 var C = INS_C(instruction); 417 var C = INS_C(instruction);
421 var valuesource = (C&0x100)?frame.f.constants:frame.reg; 418 var valuesource = (C&0x100)?frame.f.constants:frame.reg;
422 var value = valuesource[C&0xff]; 419 var value = valuesource[C&0xff];
430 var f = frame.reg[INS_A(instruction)].precall(); // return JS or LValue 427 var f = frame.reg[INS_A(instruction)].precall(); // return JS or LValue
431 var A = INS_A(instruction), B = INS_B(instruction), C = INS_C(instruction); 428 var A = INS_A(instruction), B = INS_B(instruction), C = INS_C(instruction);
432 var undefined; 429 var undefined;
433 var args = frame.reg.slice(A+1, B==0?undefined:(A+B)); 430 var args = frame.reg.slice(A+1, B==0?undefined:(A+B));
434 for(var i=args.length+1;i<f.maxStackSize;i++) 431 for(var i=args.length+1;i<f.maxStackSize;i++)
435 args[i] = new LValue("nil", null); 432 args[i] = new LValue(this, "nil", null);
436 if(typeof(f) == "function") 433 if(typeof(f) == "function")
437 { 434 {
438 // JS native function 435 // JS native function
439 var ret = f.apply(this, [args]); 436 var ret = this.call(f, args);
440 for(var i = 0; i < (C-1); i++) //FIXME: Handle C == 0 437 for(var i = 0; i < (C-1); i++) //FIXME: Handle C == 0
441 { 438 {
442 if(i < ret && i < args.length) 439 if(i < ret && i < args.length)
443 frame.reg[A+i] = args[i]; 440 frame.reg[A+i] = args[i];
444 else 441 else
445 frame.reg[A+i] = new LValue("nil", null); 442 frame.reg[A+i] = new LValue(this, "nil", null);
446 } 443 }
447 } 444 }
448 else 445 else
449 { 446 {
450 // Lua function 447 // Lua function
456 break; 453 break;
457 case OP_CLOSURE: 454 case OP_CLOSURE:
458 var prototype_id = INS_Bx(instruction); 455 var prototype_id = INS_Bx(instruction);
459 var chunk = frame.f.chunk.prototypes[prototype_id]; 456 var chunk = frame.f.chunk.prototypes[prototype_id];
460 var f = new LFunction(this, chunk, frame.f.environment); 457 var f = new LFunction(this, chunk, frame.f.environment);
461 frame.reg[INS_A(instruction)] = new LValue("function", f); 458 frame.reg[INS_A(instruction)] = new LValue(this, "function", f);
462 for(var i=0;i<chunk.numUpvalues;i++) 459 for(var i=0;i<chunk.numUpvalues;i++)
463 { 460 {
464 var upval_instruction = frame.f.instructions[frame.pc++]; 461 var upval_instruction = frame.f.instructions[frame.pc++];
465 switch(INS_OPCODE(upval_instruction)) 462 switch(INS_OPCODE(upval_instruction))
466 { 463 {
504 break; 501 break;
505 case OP_FORPREP: 502 case OP_FORPREP:
506 frame.pc+=(INS_sBx(instruction)); 503 frame.pc+=(INS_sBx(instruction));
507 var A = INS_A(instruction); 504 var A = INS_A(instruction);
508 frame.reg[A].value -= frame.reg[A+2].value; 505 frame.reg[A].value -= frame.reg[A+2].value;
509 frame.reg[A+3] = new LValue("number", null); 506 frame.reg[A+3] = new LValue(this, "number", null);
510 break; 507 break;
511 case OP_FORLOOP: 508 case OP_FORLOOP:
512 var A = INS_A(instruction); 509 var A = INS_A(instruction);
513 var RA = frame.reg[A]; 510 var RA = frame.reg[A];
514 RA.value += frame.reg[A+2].value; 511 RA.value += frame.reg[A+2].value;
541 frame.reg[INS_A(instruction)] = RB.add(RC); 538 frame.reg[INS_A(instruction)] = RB.add(RC);
542 break; 539 break;
543 case OP_SUB: 540 case OP_SUB:
544 var RB = frame.reg[INS_B(instruction)]; 541 var RB = frame.reg[INS_B(instruction)];
545 var RC = frame.reg[INS_C(instruction)]; 542 var RC = frame.reg[INS_C(instruction)];
546 frame.reg[INS_A(instruction)] = new LValue("number", RB.value - RC.value); 543 frame.reg[INS_A(instruction)] = new LValue(this, "number", RB.value - RC.value);
547 break; 544 break;
548 case OP_LT: 545 case OP_LT:
549 var RB = frame.reg[INS_B(instruction)]; 546 var RB = frame.reg[INS_B(instruction)];
550 var RC = frame.reg[INS_C(instruction)]; 547 var RC = frame.reg[INS_C(instruction)];
551 if(RB.value < RC.value) 548 if(RB.value < RC.value)
561 try{ 558 try{
562 var testvm = new LVM(); 559 var testvm = new LVM();
563 560
564 var fs=require("fs"); 561 var fs=require("fs");
565 var sys=require("sys"); 562 var sys=require("sys");
566 var c = new LBinaryChunk(fs.readFileSync("luac.out", "binary")); 563 var c = new LBinaryChunk(testvm, fs.readFileSync("luac.out", "binary"));
567 564
568 var default_environment = new LValue("table", {}); 565 var default_environment = testvm.LValue({});
569 566
570 // Standard library 567 // Standard library
571 568
572 var baselib = { 569 var baselib = {
573 print: function (args) 570 print: function (args)
587 } 584 }
588 }; 585 };
589 586
590 for(var name in baselib) 587 for(var name in baselib)
591 { 588 {
592 default_environment.setIndex(LValueFromString(name), LValueFromFunction(testvm, baselib[name])); 589 default_environment.setIndex(testvm.LValue(name), testvm.LValue(baselib[name]));
593 } 590 }
594 591
595 // Metatable on environment to print out nil global accesses 592 // Metatable on environment to print out nil global accesses
596 var mt = new LValue("table", {}); 593 var mt = testvm.LValue({});
597 mt.setIndex( 594 mt.setIndex(
598 LValueFromString("__index"), 595 testvm.LValue("__index"),
599 LValueFromFunction(testvm, function (t, k) { sys.puts("Access of nil global: "+k); }) 596 testvm.LValue(function (t, k) { sys.puts("Access of nil global: "+k); })
600 ); 597 );
601 default_environment.setMetatable(mt); 598 default_environment.setMetatable(mt);
602 599
603 600
604 var f = new LFunction(testvm, c, default_environment); 601 var f = new LFunction(testvm, c, default_environment);

mercurial