mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
work on new JS backend
This commit is contained in:
parent
56239b4a23
commit
c2d8349c48
@ -5,7 +5,7 @@ import v.pref
|
||||
import v.util
|
||||
import v.builder
|
||||
import v.gen.js
|
||||
|
||||
import v.gen.newjs
|
||||
pub fn start() {
|
||||
mut args_and_flags := util.join_env_vflags_and_os_args()[1..]
|
||||
prefs, _ := pref.parse_args([], args_and_flags)
|
||||
@ -43,5 +43,7 @@ pub fn gen_js(mut b builder.Builder, v_files []string) string {
|
||||
util.timing_start('JS GEN')
|
||||
res := js.gen(b.parsed_files, b.table, b.pref)
|
||||
util.timing_measure('JS GEN')
|
||||
|
||||
newjs.foo()
|
||||
return res
|
||||
}
|
||||
|
49
vlib/v/gen/newjs/js.v
Normal file
49
vlib/v/gen/newjs/js.v
Normal file
@ -0,0 +1,49 @@
|
||||
module newjs
|
||||
|
||||
import strings
|
||||
import v.ast
|
||||
import v.token
|
||||
import v.pref
|
||||
import v.util
|
||||
import v.util.version
|
||||
import v.depgraph
|
||||
|
||||
|
||||
[heap]
|
||||
struct JsGen {
|
||||
pref &pref.Preferences
|
||||
mut:
|
||||
table &ast.Table = unsafe { nil }
|
||||
definitions strings.Builder
|
||||
}
|
||||
|
||||
[heap]
|
||||
struct ModuleContext {
|
||||
mut:
|
||||
gen &JsGen = unsafe { nil }
|
||||
mod ast.Module
|
||||
files []string
|
||||
indentation int = 1
|
||||
minify bool
|
||||
depdendencies map[&ast.Ident]bool
|
||||
escaping_vars map[&ast.Var]bool
|
||||
object_names map[&ast.Ident]string
|
||||
mod_vars map[string]string
|
||||
}
|
||||
|
||||
struct FuncContext {
|
||||
mut:
|
||||
mod_ctx &ModuleContext = unsafe { nil }
|
||||
parent &FuncContext = unsafe { nil }
|
||||
all_vars map[string]int = {}
|
||||
local_vars []string
|
||||
result_names []ast.Expr
|
||||
output []byte
|
||||
delayed_output []byte
|
||||
pos_available bool
|
||||
}
|
||||
|
||||
|
||||
pub fn foo() {
|
||||
|
||||
}
|
373
vlib/v/gen/newjs/prelude/goroutines.js
Normal file
373
vlib/v/gen/newjs/prelude/goroutines.js
Normal file
@ -0,0 +1,373 @@
|
||||
|
||||
var $stackDepthOffset = 0;
|
||||
var $getStackDepth = function () {
|
||||
var err = new Error();
|
||||
if (err.stack === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
return $stackDepthOffset + err.stack.split("\n").length;
|
||||
};
|
||||
var $panicStackDepth = null, $panicValue;
|
||||
var $callDeferred = function (deferred, jsErr, fromPanic) {
|
||||
if (!fromPanic && deferred !== null && $curGoroutine.deferStack.indexOf(deferred) == -1) {
|
||||
throw jsErr;
|
||||
}
|
||||
if (jsErr !== null) {
|
||||
var newErr = null;
|
||||
try {
|
||||
$panic(new $jsErrorPtr(jsErr));
|
||||
} catch (err) {
|
||||
newErr = err;
|
||||
}
|
||||
$callDeferred(deferred, newErr);
|
||||
return;
|
||||
}
|
||||
if ($curGoroutine.asleep) {
|
||||
return;
|
||||
}
|
||||
$stackDepthOffset--;
|
||||
var outerPanicStackDepth = $panicStackDepth;
|
||||
var outerPanicValue = $panicValue;
|
||||
var localPanicValue = $curGoroutine.panicStack.pop();
|
||||
if (localPanicValue !== undefined) {
|
||||
$panicStackDepth = $getStackDepth();
|
||||
$panicValue = localPanicValue;
|
||||
}
|
||||
try {
|
||||
while (true) {
|
||||
if (deferred === null) {
|
||||
deferred = $curGoroutine.deferStack[$curGoroutine.deferStack.length - 1];
|
||||
if (deferred === undefined) {
|
||||
/* The panic reached the top of the stack. Clear it and throw it as a JavaScript error. */
|
||||
$panicStackDepth = null;
|
||||
if (localPanicValue.Object instanceof Error) {
|
||||
throw localPanicValue.Object;
|
||||
}
|
||||
var msg;
|
||||
if (localPanicValue.constructor === $String) {
|
||||
msg = localPanicValue.$val;
|
||||
} else if (localPanicValue.Error !== undefined) {
|
||||
msg = localPanicValue.Error();
|
||||
} else if (localPanicValue.String !== undefined) {
|
||||
msg = localPanicValue.String();
|
||||
} else {
|
||||
msg = localPanicValue;
|
||||
}
|
||||
throw new Error(msg);
|
||||
}
|
||||
}
|
||||
var call = deferred.pop();
|
||||
if (call === undefined) {
|
||||
$curGoroutine.deferStack.pop();
|
||||
if (localPanicValue !== undefined) {
|
||||
deferred = null;
|
||||
continue;
|
||||
}
|
||||
return;
|
||||
}
|
||||
var r = call[0].apply(call[2], call[1]);
|
||||
if (r && r.$blk !== undefined) {
|
||||
deferred.push([r.$blk, [], r]);
|
||||
if (fromPanic) {
|
||||
throw null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (localPanicValue !== undefined && $panicStackDepth === null) {
|
||||
/* error was recovered */
|
||||
if (fromPanic) {
|
||||
throw null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Deferred function threw a JavaScript exception or tries to unwind stack
|
||||
// to the point where a panic was handled.
|
||||
if (fromPanic) {
|
||||
// Re-throw the exception to reach deferral execution call at the end
|
||||
// of the function.
|
||||
throw e;
|
||||
}
|
||||
// We are at the end of the function, handle the error or re-throw to
|
||||
// continue unwinding if necessary, or simply stop unwinding if we got far
|
||||
// enough.
|
||||
$callDeferred(deferred, e, fromPanic);
|
||||
} finally {
|
||||
if (localPanicValue !== undefined) {
|
||||
if ($panicStackDepth !== null) {
|
||||
$curGoroutine.panicStack.push(localPanicValue);
|
||||
}
|
||||
$panicStackDepth = outerPanicStackDepth;
|
||||
$panicValue = outerPanicValue;
|
||||
}
|
||||
$stackDepthOffset++;
|
||||
}
|
||||
};
|
||||
var $panic = function (value) {
|
||||
$curGoroutine.panicStack.push(value);
|
||||
$callDeferred(null, null, true);
|
||||
};
|
||||
var $recover = function () {
|
||||
if ($panicStackDepth === null || ($panicStackDepth !== undefined && $panicStackDepth !== $getStackDepth() - 2)) {
|
||||
return $ifaceNil;
|
||||
}
|
||||
$panicStackDepth = null;
|
||||
return $panicValue;
|
||||
};
|
||||
var $throw = function (err) { throw err; };
|
||||
var $noGoroutine = { asleep: false, exit: false, deferStack: [], panicStack: [] };
|
||||
var $curGoroutine = $noGoroutine, $totalGoroutines = 0, $awakeGoroutines = 0, $checkForDeadlock = true, $exportedFunctions = 0;
|
||||
var $mainFinished = false;
|
||||
var $go = function (fun, args) {
|
||||
$totalGoroutines++;
|
||||
$awakeGoroutines++;
|
||||
var $goroutine = function () {
|
||||
try {
|
||||
$curGoroutine = $goroutine;
|
||||
var r = fun.apply(undefined, args);
|
||||
if (r && r.$blk !== undefined) {
|
||||
fun = function () { return r.$blk(); };
|
||||
args = [];
|
||||
return;
|
||||
}
|
||||
$goroutine.exit = true;
|
||||
} catch (err) {
|
||||
if (!$goroutine.exit) {
|
||||
throw err;
|
||||
}
|
||||
} finally {
|
||||
$curGoroutine = $noGoroutine;
|
||||
if ($goroutine.exit) { /* also set by runtime.Goexit() */
|
||||
$totalGoroutines--;
|
||||
$goroutine.asleep = true;
|
||||
}
|
||||
if ($goroutine.asleep) {
|
||||
$awakeGoroutines--;
|
||||
if (!$mainFinished && $awakeGoroutines === 0 && $checkForDeadlock && $exportedFunctions === 0) {
|
||||
console.error("fatal error: all goroutines are asleep - deadlock!");
|
||||
if ($global.process !== undefined) {
|
||||
$global.process.exit(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
$goroutine.asleep = false;
|
||||
$goroutine.exit = false;
|
||||
$goroutine.deferStack = [];
|
||||
$goroutine.panicStack = [];
|
||||
$schedule($goroutine);
|
||||
};
|
||||
var $scheduled = [];
|
||||
var $runScheduled = function () {
|
||||
// For nested setTimeout calls browsers enforce 4ms minimum delay. We minimize
|
||||
// the effect of this penalty by queueing the timer preemptively before we run
|
||||
// the goroutines, and later cancelling it if it turns out unneeded. See:
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#nested_timeouts
|
||||
var nextRun = setTimeout($runScheduled);
|
||||
try {
|
||||
var start = Date.now();
|
||||
var r;
|
||||
while ((r = $scheduled.shift()) !== undefined) {
|
||||
r();
|
||||
// We need to interrupt this loop in order to allow the event loop to
|
||||
// process timers, IO, etc. However, invoking scheduling through
|
||||
// setTimeout is ~1000 times more expensive, so we amortize this cost by
|
||||
// looping until the 4ms minimal delay has elapsed (assuming there are
|
||||
// scheduled goroutines to run), and then yield to the event loop.
|
||||
var elapsed = Date.now() - start;
|
||||
if (elapsed > 4 || elapsed < 0) { break; }
|
||||
}
|
||||
} finally {
|
||||
if ($scheduled.length == 0) {
|
||||
// Cancel scheduling pass if there's nothing to run.
|
||||
clearTimeout(nextRun);
|
||||
}
|
||||
}
|
||||
};
|
||||
var $schedule = function (goroutine) {
|
||||
if (goroutine.asleep) {
|
||||
goroutine.asleep = false;
|
||||
$awakeGoroutines++;
|
||||
}
|
||||
$scheduled.push(goroutine);
|
||||
if ($curGoroutine === $noGoroutine) {
|
||||
$runScheduled();
|
||||
}
|
||||
};
|
||||
var $setTimeout = function (f, t) {
|
||||
$awakeGoroutines++;
|
||||
return setTimeout(function () {
|
||||
$awakeGoroutines--;
|
||||
f();
|
||||
}, t);
|
||||
};
|
||||
var $block = function () {
|
||||
if ($curGoroutine === $noGoroutine) {
|
||||
$throwRuntimeError("cannot block in JavaScript callback, fix by wrapping code in goroutine");
|
||||
}
|
||||
$curGoroutine.asleep = true;
|
||||
};
|
||||
var $restore = function (context, params) {
|
||||
if (context !== undefined && context.$blk !== undefined) {
|
||||
return context;
|
||||
}
|
||||
return params;
|
||||
}
|
||||
var $send = function (chan, value) {
|
||||
if (chan.$closed) {
|
||||
$throwRuntimeError("send on closed channel");
|
||||
}
|
||||
var queuedRecv = chan.$recvQueue.shift();
|
||||
if (queuedRecv !== undefined) {
|
||||
queuedRecv([value, true]);
|
||||
return;
|
||||
}
|
||||
if (chan.$buffer.length < chan.$capacity) {
|
||||
chan.$buffer.push(value);
|
||||
return;
|
||||
}
|
||||
var thisGoroutine = $curGoroutine;
|
||||
var closedDuringSend;
|
||||
chan.$sendQueue.push(function (closed) {
|
||||
closedDuringSend = closed;
|
||||
$schedule(thisGoroutine);
|
||||
return value;
|
||||
});
|
||||
$block();
|
||||
return {
|
||||
$blk: function () {
|
||||
if (closedDuringSend) {
|
||||
$throwRuntimeError("send on closed channel");
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
var $recv = function (chan) {
|
||||
var queuedSend = chan.$sendQueue.shift();
|
||||
if (queuedSend !== undefined) {
|
||||
chan.$buffer.push(queuedSend(false));
|
||||
}
|
||||
var bufferedValue = chan.$buffer.shift();
|
||||
if (bufferedValue !== undefined) {
|
||||
return [bufferedValue, true];
|
||||
}
|
||||
if (chan.$closed) {
|
||||
return [chan.$elem.zero(), false];
|
||||
}
|
||||
var thisGoroutine = $curGoroutine;
|
||||
var f = { $blk: function () { return this.value; } };
|
||||
var queueEntry = function (v) {
|
||||
f.value = v;
|
||||
$schedule(thisGoroutine);
|
||||
};
|
||||
chan.$recvQueue.push(queueEntry);
|
||||
$block();
|
||||
return f;
|
||||
};
|
||||
var $close = function (chan) {
|
||||
if (chan.$closed) {
|
||||
$throwRuntimeError("close of closed channel");
|
||||
}
|
||||
chan.$closed = true;
|
||||
while (true) {
|
||||
var queuedSend = chan.$sendQueue.shift();
|
||||
if (queuedSend === undefined) {
|
||||
break;
|
||||
}
|
||||
queuedSend(true); /* will panic */
|
||||
}
|
||||
while (true) {
|
||||
var queuedRecv = chan.$recvQueue.shift();
|
||||
if (queuedRecv === undefined) {
|
||||
break;
|
||||
}
|
||||
queuedRecv([chan.$elem.zero(), false]);
|
||||
}
|
||||
};
|
||||
var $select = function (comms) {
|
||||
var ready = [];
|
||||
var selection = -1;
|
||||
for (var i = 0; i < comms.length; i++) {
|
||||
var comm = comms[i];
|
||||
var chan = comm[0];
|
||||
switch (comm.length) {
|
||||
case 0: /* default */
|
||||
selection = i;
|
||||
break;
|
||||
case 1: /* recv */
|
||||
if (chan.$sendQueue.length !== 0 || chan.$buffer.length !== 0 || chan.$closed) {
|
||||
ready.push(i);
|
||||
}
|
||||
break;
|
||||
case 2: /* send */
|
||||
if (chan.$closed) {
|
||||
$throwRuntimeError("send on closed channel");
|
||||
}
|
||||
if (chan.$recvQueue.length !== 0 || chan.$buffer.length < chan.$capacity) {
|
||||
ready.push(i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ready.length !== 0) {
|
||||
selection = ready[Math.floor(Math.random() * ready.length)];
|
||||
}
|
||||
if (selection !== -1) {
|
||||
var comm = comms[selection];
|
||||
switch (comm.length) {
|
||||
case 0: /* default */
|
||||
return [selection];
|
||||
case 1: /* recv */
|
||||
return [selection, $recv(comm[0])];
|
||||
case 2: /* send */
|
||||
$send(comm[0], comm[1]);
|
||||
return [selection];
|
||||
}
|
||||
}
|
||||
var entries = [];
|
||||
var thisGoroutine = $curGoroutine;
|
||||
var f = { $blk: function () { return this.selection; } };
|
||||
var removeFromQueues = function () {
|
||||
for (var i = 0; i < entries.length; i++) {
|
||||
var entry = entries[i];
|
||||
var queue = entry[0];
|
||||
var index = queue.indexOf(entry[1]);
|
||||
if (index !== -1) {
|
||||
queue.splice(index, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
for (var i = 0; i < comms.length; i++) {
|
||||
(function (i) {
|
||||
var comm = comms[i];
|
||||
switch (comm.length) {
|
||||
case 1: /* recv */
|
||||
var queueEntry = function (value) {
|
||||
f.selection = [i, value];
|
||||
removeFromQueues();
|
||||
$schedule(thisGoroutine);
|
||||
};
|
||||
entries.push([comm[0].$recvQueue, queueEntry]);
|
||||
comm[0].$recvQueue.push(queueEntry);
|
||||
break;
|
||||
case 2: /* send */
|
||||
var queueEntry = function () {
|
||||
if (comm[0].$closed) {
|
||||
$throwRuntimeError("send on closed channel");
|
||||
}
|
||||
f.selection = [i];
|
||||
removeFromQueues();
|
||||
$schedule(thisGoroutine);
|
||||
return comm[1];
|
||||
};
|
||||
entries.push([comm[0].$sendQueue, queueEntry]);
|
||||
comm[0].$sendQueue.push(queueEntry);
|
||||
break;
|
||||
}
|
||||
})(i);
|
||||
}
|
||||
$block();
|
||||
return f;
|
||||
};
|
390
vlib/v/gen/newjs/prelude/jsmapping.js
Normal file
390
vlib/v/gen/newjs/prelude/jsmapping.js
Normal file
@ -0,0 +1,390 @@
|
||||
var $jsObjectPtr, $jsErrorPtr;
|
||||
var $needsExternalization = function(t) {
|
||||
switch (t.kind) {
|
||||
case $kindBool:
|
||||
case $kindInt:
|
||||
case $kindInt8:
|
||||
case $kindInt16:
|
||||
case $kindInt32:
|
||||
case $kindUint:
|
||||
case $kindUint8:
|
||||
case $kindUint16:
|
||||
case $kindUint32:
|
||||
case $kindUintptr:
|
||||
case $kindFloat32:
|
||||
case $kindFloat64:
|
||||
return false;
|
||||
default:
|
||||
return t !== $jsObjectPtr;
|
||||
}
|
||||
};
|
||||
var $externalize = function(v, t, makeWrapper) {
|
||||
if (t === $jsObjectPtr) {
|
||||
return v;
|
||||
}
|
||||
switch (t.kind) {
|
||||
case $kindBool:
|
||||
case $kindInt:
|
||||
case $kindInt8:
|
||||
case $kindInt16:
|
||||
case $kindInt32:
|
||||
case $kindUint:
|
||||
case $kindUint8:
|
||||
case $kindUint16:
|
||||
case $kindUint32:
|
||||
case $kindUintptr:
|
||||
case $kindFloat32:
|
||||
case $kindFloat64:
|
||||
return v;
|
||||
case $kindInt64:
|
||||
case $kindUint64:
|
||||
return $flatten64(v);
|
||||
case $kindArray:
|
||||
if ($needsExternalization(t.elem)) {
|
||||
return $mapArray(v, function(e) { return $externalize(e, t.elem, makeWrapper); });
|
||||
}
|
||||
return v;
|
||||
case $kindFunc:
|
||||
return $externalizeFunction(v, t, false, makeWrapper);
|
||||
case $kindInterface:
|
||||
if (v === $ifaceNil) {
|
||||
return null;
|
||||
}
|
||||
if (v.constructor === $jsObjectPtr) {
|
||||
return v.$val.object;
|
||||
}
|
||||
return $externalize(v.$val, v.constructor, makeWrapper);
|
||||
case $kindMap:
|
||||
var m = {};
|
||||
var keys = Array.from(v.keys());
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
var entry = v.get(keys[i]);
|
||||
m[$externalize(entry.k, t.key, makeWrapper)] = $externalize(entry.v, t.elem, makeWrapper);
|
||||
}
|
||||
return m;
|
||||
case $kindPtr:
|
||||
if (v === t.nil) {
|
||||
return null;
|
||||
}
|
||||
return $externalize(v.$get(), t.elem, makeWrapper);
|
||||
case $kindSlice:
|
||||
if ($needsExternalization(t.elem)) {
|
||||
return $mapArray($sliceToNativeArray(v), function(e) { return $externalize(e, t.elem, makeWrapper); });
|
||||
}
|
||||
return $sliceToNativeArray(v);
|
||||
case $kindString:
|
||||
if ($isASCII(v)) {
|
||||
return v;
|
||||
}
|
||||
var s = "", r;
|
||||
for (var i = 0; i < v.length; i += r[1]) {
|
||||
r = $decodeRune(v, i);
|
||||
var c = r[0];
|
||||
if (c > 0xFFFF) {
|
||||
var h = Math.floor((c - 0x10000) / 0x400) + 0xD800;
|
||||
var l = (c - 0x10000) % 0x400 + 0xDC00;
|
||||
s += String.fromCharCode(h, l);
|
||||
continue;
|
||||
}
|
||||
s += String.fromCharCode(c);
|
||||
}
|
||||
return s;
|
||||
case $kindStruct:
|
||||
var timePkg = $packages["time"];
|
||||
if (timePkg !== undefined && v.constructor === timePkg.Time.ptr) {
|
||||
var milli = $div64(v.UnixNano(), new $Int64(0, 1000000));
|
||||
return new Date($flatten64(milli));
|
||||
}
|
||||
var noJsObject = {};
|
||||
var searchJsObject = function(v, t) {
|
||||
if (t === $jsObjectPtr) {
|
||||
return v;
|
||||
}
|
||||
switch (t.kind) {
|
||||
case $kindPtr:
|
||||
if (v === t.nil) {
|
||||
return noJsObject;
|
||||
}
|
||||
return searchJsObject(v.$get(), t.elem);
|
||||
case $kindStruct:
|
||||
var f = t.fields[0];
|
||||
return searchJsObject(v[f.prop], f.typ);
|
||||
case $kindInterface:
|
||||
return searchJsObject(v.$val, v.constructor);
|
||||
default:
|
||||
return noJsObject;
|
||||
}
|
||||
};
|
||||
var o = searchJsObject(v, t);
|
||||
if (o !== noJsObject) {
|
||||
return o;
|
||||
}
|
||||
if (makeWrapper !== undefined) {
|
||||
return makeWrapper(v);
|
||||
}
|
||||
o = {};
|
||||
for (var i = 0; i < t.fields.length; i++) {
|
||||
var f = t.fields[i];
|
||||
if (!f.exported) {
|
||||
continue;
|
||||
}
|
||||
o[f.name] = $externalize(v[f.prop], f.typ, makeWrapper);
|
||||
}
|
||||
return o;
|
||||
}
|
||||
$throwRuntimeError("cannot externalize " + t.string);
|
||||
};
|
||||
var $externalizeFunction = function(v, t, passThis, makeWrapper) {
|
||||
if (v === $throwNilPointerError) {
|
||||
return null;
|
||||
}
|
||||
if (v.$externalizeWrapper === undefined) {
|
||||
$checkForDeadlock = false;
|
||||
v.$externalizeWrapper = function() {
|
||||
var args = [];
|
||||
for (var i = 0; i < t.params.length; i++) {
|
||||
if (t.variadic && i === t.params.length - 1) {
|
||||
var vt = t.params[i].elem, varargs = [];
|
||||
for (var j = i; j < arguments.length; j++) {
|
||||
varargs.push($internalize(arguments[j], vt, makeWrapper));
|
||||
}
|
||||
args.push(new (t.params[i])(varargs));
|
||||
break;
|
||||
}
|
||||
args.push($internalize(arguments[i], t.params[i], makeWrapper));
|
||||
}
|
||||
var result = v.apply(passThis ? this : undefined, args);
|
||||
switch (t.results.length) {
|
||||
case 0:
|
||||
return;
|
||||
case 1:
|
||||
return $externalize($copyIfRequired(result, t.results[0]), t.results[0], makeWrapper);
|
||||
default:
|
||||
for (var i = 0; i < t.results.length; i++) {
|
||||
result[i] = $externalize($copyIfRequired(result[i], t.results[i]), t.results[i], makeWrapper);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
}
|
||||
return v.$externalizeWrapper;
|
||||
};
|
||||
var $internalize = function(v, t, recv, seen, makeWrapper) {
|
||||
if (t === $jsObjectPtr) {
|
||||
return v;
|
||||
}
|
||||
if (t === $jsObjectPtr.elem) {
|
||||
$throwRuntimeError("cannot internalize js.Object, use *js.Object instead");
|
||||
}
|
||||
if (v && v.__internal_object__ !== undefined) {
|
||||
return $assertType(v.__internal_object__, t, false);
|
||||
}
|
||||
var timePkg = $packages["time"];
|
||||
if (timePkg !== undefined && t === timePkg.Time) {
|
||||
if (!(v !== null && v !== undefined && v.constructor === Date)) {
|
||||
$throwRuntimeError("cannot internalize time.Time from " + typeof v + ", must be Date");
|
||||
}
|
||||
return timePkg.Unix(new $Int64(0, 0), new $Int64(0, v.getTime() * 1000000));
|
||||
}
|
||||
// Cache for values we've already internalized in order to deal with circular
|
||||
// references.
|
||||
if (seen === undefined) { seen = new Map(); }
|
||||
if (!seen.has(t)) { seen.set(t, new Map()); }
|
||||
if (seen.get(t).has(v)) { return seen.get(t).get(v); }
|
||||
switch (t.kind) {
|
||||
case $kindBool:
|
||||
return !!v;
|
||||
case $kindInt:
|
||||
return parseInt(v);
|
||||
case $kindInt8:
|
||||
return parseInt(v) << 24 >> 24;
|
||||
case $kindInt16:
|
||||
return parseInt(v) << 16 >> 16;
|
||||
case $kindInt32:
|
||||
return parseInt(v) >> 0;
|
||||
case $kindUint:
|
||||
return parseInt(v);
|
||||
case $kindUint8:
|
||||
return parseInt(v) << 24 >>> 24;
|
||||
case $kindUint16:
|
||||
return parseInt(v) << 16 >>> 16;
|
||||
case $kindUint32:
|
||||
case $kindUintptr:
|
||||
return parseInt(v) >>> 0;
|
||||
case $kindInt64:
|
||||
case $kindUint64:
|
||||
return new t(0, v);
|
||||
case $kindFloat32:
|
||||
case $kindFloat64:
|
||||
return parseFloat(v);
|
||||
case $kindArray:
|
||||
if (v.length !== t.len) {
|
||||
$throwRuntimeError("got array with wrong size from JavaScript native");
|
||||
}
|
||||
return $mapArray(v, function(e) { return $internalize(e, t.elem, makeWrapper); });
|
||||
case $kindFunc:
|
||||
return function() {
|
||||
var args = [];
|
||||
for (var i = 0; i < t.params.length; i++) {
|
||||
if (t.variadic && i === t.params.length - 1) {
|
||||
var vt = t.params[i].elem, varargs = arguments[i];
|
||||
for (var j = 0; j < varargs.$length; j++) {
|
||||
args.push($externalize(varargs.$array[varargs.$offset + j], vt, makeWrapper));
|
||||
}
|
||||
break;
|
||||
}
|
||||
args.push($externalize(arguments[i], t.params[i], makeWrapper));
|
||||
}
|
||||
var result = v.apply(recv, args);
|
||||
switch (t.results.length) {
|
||||
case 0:
|
||||
return;
|
||||
case 1:
|
||||
return $internalize(result, t.results[0], makeWrapper);
|
||||
default:
|
||||
for (var i = 0; i < t.results.length; i++) {
|
||||
result[i] = $internalize(result[i], t.results[i], makeWrapper);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
case $kindInterface:
|
||||
if (t.methods.length !== 0) {
|
||||
$throwRuntimeError("cannot internalize " + t.string);
|
||||
}
|
||||
if (v === null) {
|
||||
return $ifaceNil;
|
||||
}
|
||||
if (v === undefined) {
|
||||
return new $jsObjectPtr(undefined);
|
||||
}
|
||||
switch (v.constructor) {
|
||||
case Int8Array:
|
||||
return new ($sliceType($Int8))(v);
|
||||
case Int16Array:
|
||||
return new ($sliceType($Int16))(v);
|
||||
case Int32Array:
|
||||
return new ($sliceType($Int))(v);
|
||||
case Uint8Array:
|
||||
return new ($sliceType($Uint8))(v);
|
||||
case Uint16Array:
|
||||
return new ($sliceType($Uint16))(v);
|
||||
case Uint32Array:
|
||||
return new ($sliceType($Uint))(v);
|
||||
case Float32Array:
|
||||
return new ($sliceType($Float32))(v);
|
||||
case Float64Array:
|
||||
return new ($sliceType($Float64))(v);
|
||||
case Array:
|
||||
return $internalize(v, $sliceType($emptyInterface), makeWrapper);
|
||||
case Boolean:
|
||||
return new $Bool(!!v);
|
||||
case Date:
|
||||
if (timePkg === undefined) {
|
||||
/* time package is not present, internalize as &js.Object{Date} so it can be externalized into original Date. */
|
||||
return new $jsObjectPtr(v);
|
||||
}
|
||||
return new timePkg.Time($internalize(v, timePkg.Time, makeWrapper));
|
||||
case (function () { }).constructor: // is usually Function, but in Chrome extensions it is something else
|
||||
var funcType = $funcType([$sliceType($emptyInterface)], [$jsObjectPtr], true);
|
||||
return new funcType($internalize(v, funcType, makeWrapper));
|
||||
case Number:
|
||||
return new $Float64(parseFloat(v));
|
||||
case String:
|
||||
return new $String($internalize(v, $String, makeWrapper));
|
||||
default:
|
||||
if ($global.Node && v instanceof $global.Node) {
|
||||
return new $jsObjectPtr(v);
|
||||
}
|
||||
var mapType = $mapType($String, $emptyInterface);
|
||||
return new mapType($internalize(v, mapType, recv, seen, makeWrapper));
|
||||
}
|
||||
case $kindMap:
|
||||
var m = new Map();
|
||||
seen.get(t).set(v, m);
|
||||
var keys = $keys(v);
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
var k = $internalize(keys[i], t.key, recv, seen, makeWrapper);
|
||||
m.set(t.key.keyFor(k), { k: k, v: $internalize(v[keys[i]], t.elem, recv, seen, makeWrapper) });
|
||||
}
|
||||
return m;
|
||||
case $kindPtr:
|
||||
if (t.elem.kind === $kindStruct) {
|
||||
return $internalize(v, t.elem, makeWrapper);
|
||||
}
|
||||
case $kindSlice:
|
||||
return new t($mapArray(v, function(e) { return $internalize(e, t.elem, makeWrapper); }));
|
||||
case $kindString:
|
||||
v = String(v);
|
||||
if ($isASCII(v)) {
|
||||
return v;
|
||||
}
|
||||
var s = "";
|
||||
var i = 0;
|
||||
while (i < v.length) {
|
||||
var h = v.charCodeAt(i);
|
||||
if (0xD800 <= h && h <= 0xDBFF) {
|
||||
var l = v.charCodeAt(i + 1);
|
||||
var c = (h - 0xD800) * 0x400 + l - 0xDC00 + 0x10000;
|
||||
s += $encodeRune(c);
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
s += $encodeRune(h);
|
||||
i++;
|
||||
}
|
||||
return s;
|
||||
case $kindStruct:
|
||||
var noJsObject = {};
|
||||
var searchJsObject = function(t) {
|
||||
if (t === $jsObjectPtr) {
|
||||
return v;
|
||||
}
|
||||
if (t === $jsObjectPtr.elem) {
|
||||
$throwRuntimeError("cannot internalize js.Object, use *js.Object instead");
|
||||
}
|
||||
switch (t.kind) {
|
||||
case $kindPtr:
|
||||
return searchJsObject(t.elem);
|
||||
case $kindStruct:
|
||||
var f = t.fields[0];
|
||||
var o = searchJsObject(f.typ);
|
||||
if (o !== noJsObject) {
|
||||
var n = new t.ptr();
|
||||
n[f.prop] = o;
|
||||
return n;
|
||||
}
|
||||
return noJsObject;
|
||||
default:
|
||||
return noJsObject;
|
||||
}
|
||||
};
|
||||
var o = searchJsObject(t);
|
||||
if (o !== noJsObject) {
|
||||
return o;
|
||||
}
|
||||
}
|
||||
$throwRuntimeError("cannot internalize " + t.string);
|
||||
};
|
||||
var $copyIfRequired = function(v, typ) {
|
||||
// interface values
|
||||
if (v && v.constructor && v.constructor.copy) {
|
||||
return new v.constructor($clone(v.$val, v.constructor))
|
||||
}
|
||||
// array and struct values
|
||||
if (typ.copy) {
|
||||
var clone = typ.zero();
|
||||
typ.copy(clone, v);
|
||||
return clone;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
/* $isASCII reports whether string s contains only ASCII characters. */
|
||||
var $isASCII = function(s) {
|
||||
for (var i = 0; i < s.length; i++) {
|
||||
if (s.charCodeAt(i) >= 128) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
172
vlib/v/gen/newjs/util.v
Normal file
172
vlib/v/gen/newjs/util.v
Normal file
@ -0,0 +1,172 @@
|
||||
module newjs
|
||||
|
||||
import net.urllib
|
||||
import v.ast
|
||||
|
||||
fn (mut fc FuncContext) write(b []byte) int {
|
||||
fc.output << b
|
||||
|
||||
return b.len
|
||||
}
|
||||
|
||||
fn (mut fc FuncContext) print(str string) {
|
||||
fc.write('\t'.repeat(fc.mod_ctx.indentation).bytes())
|
||||
fc.write(str.bytes())
|
||||
|
||||
fc.write('\n'.bytes())
|
||||
fc.write(fc.delayed_output)
|
||||
fc.delayed_output = []
|
||||
}
|
||||
|
||||
fn (mut fc FuncContext) indent(f fn()) {
|
||||
fc.mod_ctx.indentation++
|
||||
f()
|
||||
fc.mod_ctx.indentation--
|
||||
}
|
||||
|
||||
fn (mut fc FuncContext) catch_output(indent int, f fn()) []byte {
|
||||
origoutput := fc.output
|
||||
fc.output = []
|
||||
fc.mod_ctx.indentation += indent
|
||||
f()
|
||||
caught := fc.output
|
||||
fc.output = origoutput
|
||||
fc.mod_ctx.indentation -= indent
|
||||
return caught
|
||||
}
|
||||
|
||||
fn (mut fc FuncContext) print_cond(cond bool, on_true string, on_false string) {
|
||||
if !cond {
|
||||
fc.print('/* ${on_true.replace('*/', '<star>/')} */ ${on_false}')
|
||||
return
|
||||
}
|
||||
|
||||
fc.print('${on_true}')
|
||||
}
|
||||
|
||||
fn (mut fc FuncContext) delayed(f fn()) {
|
||||
fc.delayed_output = fc.catch_output(0, f)
|
||||
}
|
||||
|
||||
fn (mut fc FuncContext) new_variable_with_level(name_ string, mod_level bool) string {
|
||||
if name_ == '' {
|
||||
panic('new_variable: empty name')
|
||||
}
|
||||
|
||||
mut name := encode_ident(name_)
|
||||
|
||||
if fc.mod_ctx.minify {
|
||||
mut i := 0
|
||||
|
||||
for {
|
||||
mut offset := int('a'[0])
|
||||
if mod_level {
|
||||
offset = int('A'[0])
|
||||
}
|
||||
|
||||
mut j := 0
|
||||
|
||||
name = ''
|
||||
|
||||
for {
|
||||
name = '${rune(offset+(j%26))}${name}'
|
||||
|
||||
j = j / 26 - 1
|
||||
if j == -1 {
|
||||
break
|
||||
}
|
||||
|
||||
if fc.all_vars[name] == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
i++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
n := fc.all_vars[name]
|
||||
fc.all_vars[name] = n + 1
|
||||
|
||||
mut var_name := name
|
||||
if n > 0 {
|
||||
var_name = '${name}\$${n}'
|
||||
}
|
||||
|
||||
if mod_level {
|
||||
mut c2 := fc.parent
|
||||
|
||||
for unsafe { c2 != nil } {
|
||||
c2.all_vars[var_name] = n + 1
|
||||
c2 = c2.parent
|
||||
}
|
||||
}
|
||||
|
||||
fc.local_vars << var_name
|
||||
|
||||
return var_name
|
||||
}
|
||||
|
||||
fn (mut fc FuncContext) new_ident(name string, t ast.Type) {
|
||||
|
||||
}
|
||||
|
||||
fn is_mod_level(fc &FuncContext, sym &ast.Ident) bool {
|
||||
return unsafe { sym.scope is nil || (sym.scope == fc.mod_ctx.gen.table.global_scope || sym.scope.parent == fc.mod_ctx.gen.table.global_scope) }
|
||||
}
|
||||
|
||||
fn (mut fc FuncContext) ident_name(o &ast.Ident) string {
|
||||
if is_mod_level(fc, o) {
|
||||
fc.mod_ctx.depdendencies[o] = true
|
||||
|
||||
if o.mod != fc.mod_ctx.mod.name {
|
||||
return '${fc.mod_var_str(o.mod)}.${o.name}'
|
||||
}
|
||||
}
|
||||
|
||||
name := fc.mod_ctx.object_names[o] or {
|
||||
name := fc.new_variable_with_level(o.name, is_mod_level(o))
|
||||
fc.mod_ctx.object_names[o] = name
|
||||
name
|
||||
}
|
||||
|
||||
if o.obj is ast.Var {
|
||||
if fc.mod_ctx.escaping_vars[o.obj] {
|
||||
return '${name}[0]'
|
||||
}
|
||||
}
|
||||
|
||||
return name
|
||||
}
|
||||
|
||||
fn (mut fc FuncContext) mod_var(mod &ast.Module) string {
|
||||
if mod.name == fc.mod_ctx.mod.name {
|
||||
return '\$mod'
|
||||
}
|
||||
|
||||
mod_var := fc.mod_ctx.mod_vars[mod.name] or {
|
||||
'\$modules["${mod.name}"]'
|
||||
}
|
||||
|
||||
return mod_var
|
||||
}
|
||||
|
||||
fn (mut fc FuncContext) mod_var_str(mod string) string {
|
||||
if mod == fc.mod_ctx.mod.name {
|
||||
return '\$mod'
|
||||
}
|
||||
|
||||
mod_var := fc.mod_ctx.mod_vars[mod] or {
|
||||
'\$modules["${mod}"]'
|
||||
}
|
||||
|
||||
return mod_var
|
||||
}
|
||||
|
||||
fn encode_ident(name string) string {
|
||||
return urllib.query_escape(name).replace('%', '$')
|
||||
}
|
||||
|
||||
fn (fc &FuncContext) write_pos() {
|
||||
// tbd
|
||||
}
|
Loading…
Reference in New Issue
Block a user