threading code deactivated; tiny C bugfix: don't report warnings to error callback
This commit is contained in:
parent
c065694598
commit
93b3c03dbd
15 changed files with 208 additions and 135 deletions
|
|
@ -1,3 +1,4 @@
|
||||||
|
Comex
|
||||||
Philippe Lhoste
|
Philippe Lhoste
|
||||||
Mario Ray Mahardhika
|
Mario Ray Mahardhika
|
||||||
Dominik Picheta
|
Dominik Picheta
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nimrod's Runtime Library
|
||||||
# (c) Copyright 2006 Andreas Rumpf
|
# (c) Copyright 2010 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
## Declaration of the Document Object Model for the ECMAScript backend.
|
## Declaration of the Document Object Model for the ECMAScript backend.
|
||||||
## (c) 2008 Andreas Rumpf
|
|
||||||
|
|
||||||
when not defined(ecmascript):
|
when not defined(ecmascript):
|
||||||
{.error: "This module only works on the ECMAScript platform".}
|
{.error: "This module only works on the ECMAScript platform".}
|
||||||
|
|
|
||||||
|
|
@ -1605,10 +1605,8 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
||||||
## can be used to mark a condition to be unlikely. This is a hint for the
|
## can be used to mark a condition to be unlikely. This is a hint for the
|
||||||
## optimizer.
|
## optimizer.
|
||||||
|
|
||||||
elif defined(ecmaScript):
|
elif defined(ecmaScript) or defined(NimrodVM):
|
||||||
include "system/ecmasys"
|
# Stubs:
|
||||||
elif defined(NimrodVM):
|
|
||||||
# Stubs for the GC interface:
|
|
||||||
proc GC_disable() = nil
|
proc GC_disable() = nil
|
||||||
proc GC_enable() = nil
|
proc GC_enable() = nil
|
||||||
proc GC_fullCollect() = nil
|
proc GC_fullCollect() = nil
|
||||||
|
|
@ -1621,15 +1619,18 @@ elif defined(NimrodVM):
|
||||||
proc getFreeMem(): int = return -1
|
proc getFreeMem(): int = return -1
|
||||||
proc getTotalMem(): int = return -1
|
proc getTotalMem(): int = return -1
|
||||||
|
|
||||||
proc cmp(x, y: string): int =
|
|
||||||
if x == y: return 0
|
|
||||||
if x < y: return -1
|
|
||||||
return 1
|
|
||||||
|
|
||||||
proc dealloc(p: pointer) = nil
|
proc dealloc(p: pointer) = nil
|
||||||
proc alloc(size: int): pointer = nil
|
proc alloc(size: int): pointer = nil
|
||||||
proc alloc0(size: int): pointer = nil
|
proc alloc0(size: int): pointer = nil
|
||||||
proc realloc(p: Pointer, newsize: int): pointer = nil
|
proc realloc(p: Pointer, newsize: int): pointer = nil
|
||||||
|
|
||||||
|
when defined(ecmaScript):
|
||||||
|
include "system/ecmasys"
|
||||||
|
elif defined(NimrodVM):
|
||||||
|
proc cmp(x, y: string): int =
|
||||||
|
if x == y: return 0
|
||||||
|
if x < y: return -1
|
||||||
|
return 1
|
||||||
|
|
||||||
{.pop.} # checks
|
{.pop.} # checks
|
||||||
{.pop.} # hints
|
{.pop.} # hints
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,12 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nimrod's Runtime Library
|
||||||
# (c) Copyright 2008 Andreas Rumpf
|
# (c) Copyright 2010 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
## Stubs for the GC interface:
|
|
||||||
|
|
||||||
proc GC_disable() = nil
|
|
||||||
proc GC_enable() = nil
|
|
||||||
proc GC_fullCollect() = nil
|
|
||||||
proc GC_setStrategy(strategy: TGC_Strategy) = nil
|
|
||||||
proc GC_enableMarkAndSweep() = nil
|
|
||||||
proc GC_disableMarkAndSweep() = nil
|
|
||||||
proc GC_getStatistics(): string = return ""
|
|
||||||
|
|
||||||
proc getOccupiedMem(): int = return -1
|
|
||||||
proc getFreeMem(): int = return -1
|
|
||||||
proc getTotalMem(): int = return -1
|
|
||||||
|
|
||||||
proc alert(s: cstring) {.importc, nodecl.}
|
proc alert(s: cstring) {.importc, nodecl.}
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
@ -130,8 +116,6 @@ proc raiseIndexError() {.compilerproc, noreturn.} =
|
||||||
proc raiseFieldError(f: string) {.compilerproc, noreturn.} =
|
proc raiseFieldError(f: string) {.compilerproc, noreturn.} =
|
||||||
raise newException(EInvalidField, f & " is not accessible")
|
raise newException(EInvalidField, f & " is not accessible")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
proc SetConstr() {.varargs, pure, compilerproc.} =
|
proc SetConstr() {.varargs, pure, compilerproc.} =
|
||||||
asm """
|
asm """
|
||||||
var result = {};
|
var result = {};
|
||||||
|
|
@ -317,61 +301,58 @@ proc ewriteln(x: cstring) =
|
||||||
else:
|
else:
|
||||||
raise newException(EInvalidValue, "<body> element does not exist yet!")
|
raise newException(EInvalidValue, "<body> element does not exist yet!")
|
||||||
|
|
||||||
proc echo*(x: int) = ewriteln($x)
|
proc rawEcho {.compilerproc.} =
|
||||||
proc echo*(x: float) = ewriteln($x)
|
var node = document.getElementsByTagName("body")[0]
|
||||||
proc echo*(x: bool) = ewriteln(if x: cstring("true") else: cstring("false"))
|
if node == nil: raise newException(EIO, "<body> element does not exist yet!")
|
||||||
proc echo*(x: string) = ewriteln(x)
|
asm """
|
||||||
proc echo*(x: cstring) = ewriteln(x)
|
for (var i = 0; i < arguments.length; ++i) {
|
||||||
|
var x = `toEcmaStr`(arguments[i]);
|
||||||
proc echo[Ty](x: Ty) =
|
`node`.appendChild(document.createTextNode(x))
|
||||||
echo(x)
|
}
|
||||||
|
"""
|
||||||
proc echo[Ty](x: openArray[Ty]) =
|
node.appendChild(document.createElement("br"))
|
||||||
for a in items(x): echo(a)
|
|
||||||
|
|
||||||
# Arithmetic:
|
# Arithmetic:
|
||||||
proc addInt(a, b: int): int {.pure, compilerproc.} =
|
proc addInt(a, b: int): int {.pure, compilerproc.} =
|
||||||
asm """
|
asm """
|
||||||
var result = `a` + `b`;
|
var result = `a` + `b`;
|
||||||
if (result > 2147483647 || result < -2147483648) raiseOverflow();
|
if (result > 2147483647 || result < -2147483648) `raiseOverflow`();
|
||||||
return result;
|
return result;
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc subInt(a, b: int): int {.pure, compilerproc.} =
|
proc subInt(a, b: int): int {.pure, compilerproc.} =
|
||||||
asm """
|
asm """
|
||||||
var result = `a` - `b`;
|
var result = `a` - `b`;
|
||||||
if (result > 2147483647 || result < -2147483648) raiseOverflow();
|
if (result > 2147483647 || result < -2147483648) `raiseOverflow`();
|
||||||
return result;
|
return result;
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc mulInt(a, b: int): int {.pure, compilerproc.} =
|
proc mulInt(a, b: int): int {.pure, compilerproc.} =
|
||||||
asm """
|
asm """
|
||||||
var result = `a` * `b`;
|
var result = `a` * `b`;
|
||||||
if (result > 2147483647 || result < -2147483648) raiseOverflow();
|
if (result > 2147483647 || result < -2147483648) `raiseOverflow`();
|
||||||
return result;
|
return result;
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc divInt(a, b: int): int {.pure, compilerproc.} =
|
proc divInt(a, b: int): int {.pure, compilerproc.} =
|
||||||
asm """
|
asm """
|
||||||
if (`b` == 0) raiseDivByZero();
|
if (`b` == 0) `raiseDivByZero`();
|
||||||
if (`b` == -1 && `a` == 2147483647) raiseOverflow();
|
if (`b` == -1 && `a` == 2147483647) `raiseOverflow`();
|
||||||
return Math.floor(`a` / `b`);
|
return Math.floor(`a` / `b`);
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc modInt(a, b: int): int {.pure, compilerproc.} =
|
proc modInt(a, b: int): int {.pure, compilerproc.} =
|
||||||
asm """
|
asm """
|
||||||
if (`b` == 0) raiseDivByZero();
|
if (`b` == 0) `raiseDivByZero`();
|
||||||
if (`b` == -1 && `a` == 2147483647) raiseOverflow();
|
if (`b` == -1 && `a` == 2147483647) `raiseOverflow`();
|
||||||
return Math.floor(`a` % `b`);
|
return Math.floor(`a` % `b`);
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
proc addInt64(a, b: int): int {.pure, compilerproc.} =
|
proc addInt64(a, b: int): int {.pure, compilerproc.} =
|
||||||
asm """
|
asm """
|
||||||
var result = `a` + `b`;
|
var result = `a` + `b`;
|
||||||
if (result > 9223372036854775807
|
if (result > 9223372036854775807
|
||||||
|| result < -9223372036854775808) raiseOverflow();
|
|| result < -9223372036854775808) `raiseOverflow`();
|
||||||
return result;
|
return result;
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
@ -379,7 +360,7 @@ proc subInt64(a, b: int): int {.pure, compilerproc.} =
|
||||||
asm """
|
asm """
|
||||||
var result = `a` - `b`;
|
var result = `a` - `b`;
|
||||||
if (result > 9223372036854775807
|
if (result > 9223372036854775807
|
||||||
|| result < -9223372036854775808) raiseOverflow();
|
|| result < -9223372036854775808) `raiseOverflow`();
|
||||||
return result;
|
return result;
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
@ -387,21 +368,21 @@ proc mulInt64(a, b: int): int {.pure, compilerproc.} =
|
||||||
asm """
|
asm """
|
||||||
var result = `a` * `b`;
|
var result = `a` * `b`;
|
||||||
if (result > 9223372036854775807
|
if (result > 9223372036854775807
|
||||||
|| result < -9223372036854775808) raiseOverflow();
|
|| result < -9223372036854775808) `raiseOverflow`();
|
||||||
return result;
|
return result;
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc divInt64(a, b: int): int {.pure, compilerproc.} =
|
proc divInt64(a, b: int): int {.pure, compilerproc.} =
|
||||||
asm """
|
asm """
|
||||||
if (`b` == 0) raiseDivByZero();
|
if (`b` == 0) `raiseDivByZero`();
|
||||||
if (`b` == -1 && `a` == 9223372036854775807) raiseOverflow();
|
if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`();
|
||||||
return Math.floor(`a` / `b`);
|
return Math.floor(`a` / `b`);
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc modInt64(a, b: int): int {.pure, compilerproc.} =
|
proc modInt64(a, b: int): int {.pure, compilerproc.} =
|
||||||
asm """
|
asm """
|
||||||
if (`b` == 0) raiseDivByZero();
|
if (`b` == 0) `raiseDivByZero`();
|
||||||
if (`b` == -1 && `a` == 9223372036854775807) raiseOverflow();
|
if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`();
|
||||||
return Math.floor(`a` % `b`);
|
return Math.floor(`a` % `b`);
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
@ -415,13 +396,13 @@ proc internalAssert(file: cstring, line: int) {.pure, compilerproc.} =
|
||||||
asm """`e`.message = "[Assertion failure] file: "+`file`+", line: "+`line`"""
|
asm """`e`.message = "[Assertion failure] file: "+`file`+", line: "+`line`"""
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
include hti
|
include "system/hti"
|
||||||
|
|
||||||
proc isFatPointer(ti: PNimType): bool =
|
proc isFatPointer(ti: PNimType): bool =
|
||||||
# This has to be consistent with the code generator!
|
# This has to be consistent with the code generator!
|
||||||
return ti.base.kind notin {tyRecord, tyRecordConstr, tyObject,
|
return ti.base.kind notin {tyObject,
|
||||||
tyArray, tyArrayConstr, tyPureObject, tyTuple,
|
tyArray, tyArrayConstr, tyPureObject, tyTuple,
|
||||||
tyEmptySet, tyOpenArray, tySet, tyVar, tyRef, tyPtr}
|
tyOpenArray, tySet, tyVar, tyRef, tyPtr}
|
||||||
|
|
||||||
proc NimCopy(x: pointer, ti: PNimType): pointer {.compilerproc.}
|
proc NimCopy(x: pointer, ti: PNimType): pointer {.compilerproc.}
|
||||||
|
|
||||||
|
|
@ -452,7 +433,7 @@ proc NimCopy(x: pointer, ti: PNimType): pointer =
|
||||||
`result`[0] = `x`[0];
|
`result`[0] = `x`[0];
|
||||||
`result`[1] = `x`[1];
|
`result`[1] = `x`[1];
|
||||||
"""
|
"""
|
||||||
of tyEmptySet, tySet:
|
of tySet:
|
||||||
asm """
|
asm """
|
||||||
`result` = {};
|
`result` = {};
|
||||||
for (var key in `x`) { `result`[key] = `x`[key]; }
|
for (var key in `x`) { `result`[key] = `x`[key]; }
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,10 @@
|
||||||
# Exception handling code. This is difficult because it has
|
# Exception handling code. This is difficult because it has
|
||||||
# to work if there is no more memory (but it doesn't yet!).
|
# to work if there is no more memory (but it doesn't yet!).
|
||||||
|
|
||||||
|
var
|
||||||
|
stackTraceNewLine* = "\n" ## undocumented feature; it is replaced by ``<br>``
|
||||||
|
## for CGI applications
|
||||||
|
|
||||||
when not defined(windows) or not defined(guiapp):
|
when not defined(windows) or not defined(guiapp):
|
||||||
proc writeToStdErr(msg: CString) = write(stdout, msg)
|
proc writeToStdErr(msg: CString) = write(stdout, msg)
|
||||||
|
|
||||||
|
|
@ -32,7 +36,7 @@ type
|
||||||
TSafePoint {.compilerproc, final.} = object
|
TSafePoint {.compilerproc, final.} = object
|
||||||
prev: PSafePoint # points to next safe point ON THE STACK
|
prev: PSafePoint # points to next safe point ON THE STACK
|
||||||
status: int
|
status: int
|
||||||
exc: ref E_Base # XXX only needed for bootstrapping
|
exc: ref E_Base # XXX only needed for bootstrapping; unused
|
||||||
context: C_JmpBuf
|
context: C_JmpBuf
|
||||||
|
|
||||||
var
|
var
|
||||||
|
|
@ -55,6 +59,58 @@ proc pushCurrentException(e: ref E_Base) {.compilerRtl, inl.} =
|
||||||
proc popCurrentException {.compilerRtl, inl.} =
|
proc popCurrentException {.compilerRtl, inl.} =
|
||||||
currException = currException.parent
|
currException = currException.parent
|
||||||
|
|
||||||
|
# some platforms have native support for stack traces:
|
||||||
|
const
|
||||||
|
nimrodStackTrace = compileOption("stacktrace")
|
||||||
|
nativeStackTrace = (defined(macosx) or defined(linux)) and
|
||||||
|
not nimrodStackTrace and false
|
||||||
|
|
||||||
|
# `nativeStackTrace` does not work for me --> deactivated for now. Maybe for
|
||||||
|
# the next release version.
|
||||||
|
|
||||||
|
when nativeStacktrace:
|
||||||
|
type
|
||||||
|
TDl_info {.importc: "Dl_info", header: "<dlfcn.h>",
|
||||||
|
final, pure.} = object
|
||||||
|
dli_fname: CString
|
||||||
|
dli_fbase: pointer
|
||||||
|
dli_sname: CString
|
||||||
|
dli_saddr: pointer
|
||||||
|
|
||||||
|
proc backtrace(symbols: ptr pointer, size: int): int {.
|
||||||
|
importc: "backtrace", header: "<execinfo.h>".}
|
||||||
|
proc dladdr(addr1: pointer, info: ptr TDl_info): int {.
|
||||||
|
importc: "dladdr", header: "<dlfcn.h>".}
|
||||||
|
|
||||||
|
var
|
||||||
|
tempAddresses: array [0..127, pointer] # cannot be allocated on the stack!
|
||||||
|
tempDlInfo: TDl_info
|
||||||
|
|
||||||
|
proc auxWriteStackTraceWithBacktrace(s: var string) =
|
||||||
|
# This is allowed to be expensive since it only happens during crashes
|
||||||
|
# (but this way you don't need manual stack tracing)
|
||||||
|
var size = backtrace(cast[ptr pointer](addr(tempAddresses)),
|
||||||
|
len(tempAddresses))
|
||||||
|
var enabled = false
|
||||||
|
for i in 0..size-1:
|
||||||
|
var dlresult = dladdr(tempAddresses[i], addr(tempDlInfo))
|
||||||
|
if enabled:
|
||||||
|
if dlresult != 0:
|
||||||
|
var oldLen = s.len
|
||||||
|
add(s, tempDlInfo.dli_fname)
|
||||||
|
if tempDlInfo.dli_sname != nil:
|
||||||
|
for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ')
|
||||||
|
add(s, tempDlInfo.dli_sname)
|
||||||
|
else:
|
||||||
|
add(s, '?')
|
||||||
|
add(s, stackTraceNewLine)
|
||||||
|
else:
|
||||||
|
if dlresult != 0 and tempDlInfo.dli_sname != nil and
|
||||||
|
c_strcmp(tempDlInfo.dli_sname, "signalHandler") == 0'i32:
|
||||||
|
# Once we're past signalHandler, we're at what the user is
|
||||||
|
# interested in
|
||||||
|
enabled = true
|
||||||
|
|
||||||
type
|
type
|
||||||
PFrame = ptr TFrame
|
PFrame = ptr TFrame
|
||||||
TFrame {.importc, nodecl, final.} = object
|
TFrame {.importc, nodecl, final.} = object
|
||||||
|
|
@ -74,9 +130,6 @@ var
|
||||||
|
|
||||||
tempFrames: array [0..127, PFrame] # cannot be allocated on the stack!
|
tempFrames: array [0..127, PFrame] # cannot be allocated on the stack!
|
||||||
|
|
||||||
stackTraceNewLine* = "\n" ## undocumented feature; it is replaced by ``<br>``
|
|
||||||
## for CGI applications
|
|
||||||
|
|
||||||
proc auxWriteStackTrace(f: PFrame, s: var string) =
|
proc auxWriteStackTrace(f: PFrame, s: var string) =
|
||||||
const
|
const
|
||||||
firstCalls = 32
|
firstCalls = 32
|
||||||
|
|
@ -120,7 +173,7 @@ proc auxWriteStackTrace(f: PFrame, s: var string) =
|
||||||
add(s, stackTraceNewLine)
|
add(s, stackTraceNewLine)
|
||||||
|
|
||||||
proc rawWriteStackTrace(s: var string) =
|
proc rawWriteStackTrace(s: var string) =
|
||||||
when compileOption("stacktrace") or compileOption("linetrace"):
|
when nimrodStackTrace:
|
||||||
if framePtr == nil:
|
if framePtr == nil:
|
||||||
add(s, "No stack traceback available")
|
add(s, "No stack traceback available")
|
||||||
add(s, stackTraceNewLine)
|
add(s, stackTraceNewLine)
|
||||||
|
|
@ -128,6 +181,10 @@ proc rawWriteStackTrace(s: var string) =
|
||||||
add(s, "Traceback (most recent call last)")
|
add(s, "Traceback (most recent call last)")
|
||||||
add(s, stackTraceNewLine)
|
add(s, stackTraceNewLine)
|
||||||
auxWriteStackTrace(framePtr, s)
|
auxWriteStackTrace(framePtr, s)
|
||||||
|
elif nativeStackTrace:
|
||||||
|
add(s, "Traceback from system (most recent call last)")
|
||||||
|
add(s, stackTraceNewLine)
|
||||||
|
auxWriteStackTraceWithBacktrace(s)
|
||||||
else:
|
else:
|
||||||
add(s, "No stack traceback available")
|
add(s, "No stack traceback available")
|
||||||
add(s, stackTraceNewLine)
|
add(s, stackTraceNewLine)
|
||||||
|
|
@ -197,11 +254,6 @@ proc WriteStackTrace() =
|
||||||
rawWriteStackTrace(s)
|
rawWriteStackTrace(s)
|
||||||
writeToStdErr(s)
|
writeToStdErr(s)
|
||||||
|
|
||||||
#proc stackTraceWrapper {.noconv.} =
|
|
||||||
# writeStackTrace()
|
|
||||||
|
|
||||||
#addQuitProc(stackTraceWrapper)
|
|
||||||
|
|
||||||
var
|
var
|
||||||
dbgAborting: bool # whether the debugger wants to abort
|
dbgAborting: bool # whether the debugger wants to abort
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -77,11 +77,13 @@ var
|
||||||
# collection, not a lock for threads!
|
# collection, not a lock for threads!
|
||||||
|
|
||||||
proc lock(gch: var TGcHeap) {.inline.} =
|
proc lock(gch: var TGcHeap) {.inline.} =
|
||||||
|
when hasThreadSupport:
|
||||||
if isMultiThreaded:
|
if isMultiThreaded:
|
||||||
Lock(gch.zctLock)
|
Lock(gch.zctLock)
|
||||||
lock(gch.cycleRootsLock)
|
lock(gch.cycleRootsLock)
|
||||||
|
|
||||||
proc unlock(gch: var TGcHeap) {.inline.} =
|
proc unlock(gch: var TGcHeap) {.inline.} =
|
||||||
|
when hasThreadSupport:
|
||||||
if isMultiThreaded:
|
if isMultiThreaded:
|
||||||
unlock(gch.zctLock)
|
unlock(gch.zctLock)
|
||||||
unlock(gch.cycleRootsLock)
|
unlock(gch.cycleRootsLock)
|
||||||
|
|
@ -202,14 +204,18 @@ proc prepareDealloc(cell: PCell) =
|
||||||
|
|
||||||
proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} =
|
proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} =
|
||||||
# we MUST access gch as a global here, because this crosses DLL boundaries!
|
# we MUST access gch as a global here, because this crosses DLL boundaries!
|
||||||
|
when hasThreadSupport:
|
||||||
if isMultiThreaded: Lock(gch.cycleRootsLock)
|
if isMultiThreaded: Lock(gch.cycleRootsLock)
|
||||||
incl(gch.cycleRoots, c)
|
incl(gch.cycleRoots, c)
|
||||||
|
when hasThreadSupport:
|
||||||
if isMultiThreaded: Unlock(gch.cycleRootsLock)
|
if isMultiThreaded: Unlock(gch.cycleRootsLock)
|
||||||
|
|
||||||
proc rtlAddZCT(c: PCell) {.rtl, inl.} =
|
proc rtlAddZCT(c: PCell) {.rtl, inl.} =
|
||||||
# we MUST access gch as a global here, because this crosses DLL boundaries!
|
# we MUST access gch as a global here, because this crosses DLL boundaries!
|
||||||
|
when hasThreadSupport:
|
||||||
if isMultiThreaded: Lock(gch.zctLock)
|
if isMultiThreaded: Lock(gch.zctLock)
|
||||||
addZCT(gch.zct, c)
|
addZCT(gch.zct, c)
|
||||||
|
when hasThreadSupport:
|
||||||
if isMultiThreaded: Unlock(gch.zctLock)
|
if isMultiThreaded: Unlock(gch.zctLock)
|
||||||
|
|
||||||
proc decRef(c: PCell) {.inline.} =
|
proc decRef(c: PCell) {.inline.} =
|
||||||
|
|
@ -480,7 +486,7 @@ proc gcMark(p: pointer) {.inline.} =
|
||||||
add(gch.decStack, cell)
|
add(gch.decStack, cell)
|
||||||
|
|
||||||
proc markThreadStacks(gch: var TGcHeap) =
|
proc markThreadStacks(gch: var TGcHeap) =
|
||||||
when isMultiThreaded:
|
when hasThreadSupport:
|
||||||
nil
|
nil
|
||||||
|
|
||||||
# ----------------- stack management --------------------------------------
|
# ----------------- stack management --------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,17 @@
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
when defined(gcc) or defined(llvm_gcc):
|
const
|
||||||
|
hasThreadSupport = false # deactivate for now: thread stack walking
|
||||||
|
# is missing!
|
||||||
|
maxThreads = 256
|
||||||
|
|
||||||
|
when (defined(gcc) or defined(llvm_gcc)) and hasThreadSupport:
|
||||||
proc sync_add_and_fetch(p: var int, val: int): int {.
|
proc sync_add_and_fetch(p: var int, val: int): int {.
|
||||||
importc: "__sync_add_and_fetch", nodecl.}
|
importc: "__sync_add_and_fetch", nodecl.}
|
||||||
proc sync_sub_and_fetch(p: var int, val: int): int {.
|
proc sync_sub_and_fetch(p: var int, val: int): int {.
|
||||||
importc: "__sync_sub_and_fetch", nodecl.}
|
importc: "__sync_sub_and_fetch", nodecl.}
|
||||||
elif defined(vcc):
|
elif defined(vcc) and hasThreadSupport:
|
||||||
proc sync_add_and_fetch(p: var int, val: int): int {.
|
proc sync_add_and_fetch(p: var int, val: int): int {.
|
||||||
importc: "NimXadd", nodecl.}
|
importc: "NimXadd", nodecl.}
|
||||||
else:
|
else:
|
||||||
|
|
@ -20,19 +25,18 @@ else:
|
||||||
inc(p, val)
|
inc(p, val)
|
||||||
result = p
|
result = p
|
||||||
|
|
||||||
const
|
var
|
||||||
isMultiThreaded* = true
|
isMultiThreaded: bool # true when prog created at least 1 thread
|
||||||
maxThreads = 256
|
|
||||||
|
|
||||||
proc atomicInc(memLoc: var int, x: int): int =
|
proc atomicInc(memLoc: var int, x: int): int =
|
||||||
when isMultiThreaded:
|
when hasThreadSupport:
|
||||||
result = sync_add_and_fetch(memLoc, x)
|
result = sync_add_and_fetch(memLoc, x)
|
||||||
else:
|
else:
|
||||||
inc(memLoc, x)
|
inc(memLoc, x)
|
||||||
result = memLoc
|
result = memLoc
|
||||||
|
|
||||||
proc atomicDec(memLoc: var int, x: int): int =
|
proc atomicDec(memLoc: var int, x: int): int =
|
||||||
when isMultiThreaded:
|
when hasThreadSupport:
|
||||||
when defined(sync_sub_and_fetch):
|
when defined(sync_sub_and_fetch):
|
||||||
result = sync_sub_and_fetch(memLoc, x)
|
result = sync_sub_and_fetch(memLoc, x)
|
||||||
else:
|
else:
|
||||||
|
|
@ -85,11 +89,8 @@ type
|
||||||
TThreadFunc* = proc (closure: pointer) {.cdecl.}
|
TThreadFunc* = proc (closure: pointer) {.cdecl.}
|
||||||
|
|
||||||
proc createThread*(t: var TThread, fn: TThreadFunc) =
|
proc createThread*(t: var TThread, fn: TThreadFunc) =
|
||||||
|
|
||||||
nil
|
nil
|
||||||
|
|
||||||
proc destroyThread*(t: var TThread) =
|
proc destroyThread*(t: var TThread) =
|
||||||
nil
|
nil
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,9 @@
|
||||||
# code!**
|
# code!**
|
||||||
|
|
||||||
import
|
import
|
||||||
ast, astalgo, strutils, nhashes, trees, platform, magicsys, extccomp, options,
|
ast, astalgo, strutils, nhashes, trees, platform, magicsys, extccomp,
|
||||||
nversion, nimsets, msgs, crc, bitsets, idents, lists, types, os, times, ropes,
|
options, nversion, nimsets, msgs, crc, bitsets, idents, lists, types, os,
|
||||||
math, passes, ccgutils, wordrecg, rnimsyn, rodread
|
times, ropes, math, passes, ccgutils, wordrecg, rnimsyn, rodread
|
||||||
|
|
||||||
proc ecmasgenPass*(): TPass
|
proc ecmasgenPass*(): TPass
|
||||||
# implementation
|
# implementation
|
||||||
|
|
@ -39,7 +39,8 @@ type
|
||||||
kind*: TEcmasTypeKind
|
kind*: TEcmasTypeKind
|
||||||
com*: PRope # computation part
|
com*: PRope # computation part
|
||||||
# address if this is a (address, index)-tuple
|
# address if this is a (address, index)-tuple
|
||||||
res*: PRope # result part; index if this is a (address, index)-tuple
|
res*: PRope # result part; index if this is an
|
||||||
|
# (address, index)-tuple
|
||||||
|
|
||||||
TBlock{.final.} = object
|
TBlock{.final.} = object
|
||||||
id*: int # the ID of the label; positive means that it
|
id*: int # the ID of the label; positive means that it
|
||||||
|
|
@ -886,12 +887,11 @@ proc genDeref(p: var TProc, n: PNode, r: var TCompRes) =
|
||||||
if a.kind != etyBaseIndex: InternalError(n.info, "genDeref")
|
if a.kind != etyBaseIndex: InternalError(n.info, "genDeref")
|
||||||
r.res = ropef("$1[$2]", [a.com, a.res])
|
r.res = ropef("$1[$2]", [a.com, a.res])
|
||||||
|
|
||||||
proc genCall(p: var TProc, n: PNode, r: var TCompRes) =
|
proc genArgs(p: var TProc, n: PNode, r: var TCompRes) =
|
||||||
var a: TCompRes
|
|
||||||
gen(p, n.sons[0], r)
|
|
||||||
app(r.res, "(")
|
app(r.res, "(")
|
||||||
for i in countup(1, sonsLen(n) - 1):
|
for i in countup(1, sonsLen(n) - 1):
|
||||||
if i > 1: app(r.res, ", ")
|
if i > 1: app(r.res, ", ")
|
||||||
|
var a: TCompRes
|
||||||
gen(p, n.sons[i], a)
|
gen(p, n.sons[i], a)
|
||||||
if a.kind == etyBaseIndex:
|
if a.kind == etyBaseIndex:
|
||||||
app(r.res, a.com)
|
app(r.res, a.com)
|
||||||
|
|
@ -901,6 +901,14 @@ proc genCall(p: var TProc, n: PNode, r: var TCompRes) =
|
||||||
app(r.res, mergeExpr(a))
|
app(r.res, mergeExpr(a))
|
||||||
app(r.res, ")")
|
app(r.res, ")")
|
||||||
|
|
||||||
|
proc genCall(p: var TProc, n: PNode, r: var TCompRes) =
|
||||||
|
gen(p, n.sons[0], r)
|
||||||
|
genArgs(p, n, r)
|
||||||
|
|
||||||
|
proc genEcho(p: var TProc, n: PNode, r: var TCompRes) =
|
||||||
|
app(r.res, "rawEcho")
|
||||||
|
genArgs(p, n, r)
|
||||||
|
|
||||||
proc putToSeq(s: string, indirect: bool): PRope =
|
proc putToSeq(s: string, indirect: bool): PRope =
|
||||||
result = toRope(s)
|
result = toRope(s)
|
||||||
if indirect: result = ropef("[$1]", [result])
|
if indirect: result = ropef("[$1]", [result])
|
||||||
|
|
@ -1124,6 +1132,8 @@ proc genMagic(p: var TProc, n: PNode, r: var TCompRes) =
|
||||||
of mInSet: binaryExpr(p, n, r, "", "($1[$2] != undefined)")
|
of mInSet: binaryExpr(p, n, r, "", "($1[$2] != undefined)")
|
||||||
of mNLen..mNError:
|
of mNLen..mNError:
|
||||||
liMessage(n.info, errCannotGenerateCodeForX, n.sons[0].sym.name.s)
|
liMessage(n.info, errCannotGenerateCodeForX, n.sons[0].sym.name.s)
|
||||||
|
of mNewSeq: binaryStmt(p, n, r, "", "$1 = new Array($2)")
|
||||||
|
of mEcho: genEcho(p, n, r)
|
||||||
else:
|
else:
|
||||||
genCall(p, n, r)
|
genCall(p, n, r)
|
||||||
#else internalError(e.info, 'genMagic: ' + magicToStr[op]);
|
#else internalError(e.info, 'genMagic: ' + magicToStr[op]);
|
||||||
|
|
@ -1389,7 +1399,7 @@ proc newModule(module: PSym, filename: string): BModule =
|
||||||
|
|
||||||
proc genHeader(): PRope =
|
proc genHeader(): PRope =
|
||||||
result = ropef("/* Generated by the Nimrod Compiler v$1 */$n" &
|
result = ropef("/* Generated by the Nimrod Compiler v$1 */$n" &
|
||||||
"/* (c) 2008 Andreas Rumpf */$n$n" & "$nvar Globals = this;$n" &
|
"/* (c) 2010 Andreas Rumpf */$n$n" & "$nvar Globals = this;$n" &
|
||||||
"var framePtr = null;$n" & "var excHandler = null;$n",
|
"var framePtr = null;$n" & "var excHandler = null;$n",
|
||||||
[toRope(versionAsString)])
|
[toRope(versionAsString)])
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -203,7 +203,7 @@ proc MainCommand(cmd, filename: string) =
|
||||||
gCmd = cmdCompileToCpp
|
gCmd = cmdCompileToCpp
|
||||||
wantFile(filename)
|
wantFile(filename)
|
||||||
CommandCompileToC(filename)
|
CommandCompileToC(filename)
|
||||||
of wCompileToEcmaScript:
|
of wCompileToEcmaScript, wJs:
|
||||||
gCmd = cmdCompileToEcmaScript
|
gCmd = cmdCompileToEcmaScript
|
||||||
wantFile(filename)
|
wantFile(filename)
|
||||||
CommandCompileToEcmaScript(filename)
|
CommandCompileToEcmaScript(filename)
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ type
|
||||||
wCc, wGenscript, wCheckPoint, wCheckPoints, wNoMain, wSubsChar,
|
wCc, wGenscript, wCheckPoint, wCheckPoints, wNoMain, wSubsChar,
|
||||||
wAcyclic, wIndex,
|
wAcyclic, wIndex,
|
||||||
wCompileToC, wCompileToCpp, wCompileToEcmaScript, wCompileToLLVM, wPretty,
|
wCompileToC, wCompileToCpp, wCompileToEcmaScript, wCompileToLLVM, wPretty,
|
||||||
wDoc, wGenDepend, wListDef, wCheck, wParse, wScan, wLazy,
|
wDoc, wGenDepend, wListDef, wCheck, wParse, wScan, wJs,
|
||||||
wRst2html, wRst2tex, wI,
|
wRst2html, wRst2tex, wI,
|
||||||
wWrite, wPutEnv, wPrependEnv, wAppendEnv, wThreadVar
|
wWrite, wPutEnv, wPrependEnv, wAppendEnv, wThreadVar
|
||||||
|
|
||||||
|
|
@ -103,7 +103,7 @@ const
|
||||||
"nomain", "subschar", "acyclic", "index",
|
"nomain", "subschar", "acyclic", "index",
|
||||||
"compiletoc", "compiletocpp", "compiletoecmascript", "compiletollvm",
|
"compiletoc", "compiletocpp", "compiletoecmascript", "compiletollvm",
|
||||||
"pretty", "doc", "gendepend", "listdef", "check", "parse", "scan",
|
"pretty", "doc", "gendepend", "listdef", "check", "parse", "scan",
|
||||||
"lazy", "rst2html", "rst2tex", "i",
|
"js", "rst2html", "rst2tex", "i",
|
||||||
"write", "putenv", "prependenv", "appendenv", "threadvar"]
|
"write", "putenv", "prependenv", "appendenv", "threadvar"]
|
||||||
|
|
||||||
proc whichKeyword*(id: PIdent): TSpecialWord
|
proc whichKeyword*(id: PIdent): TSpecialWord
|
||||||
|
|
|
||||||
|
|
@ -2,18 +2,18 @@
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||||
<!-- This has been written by hand. (c) 2008 Andreas Rumpf -->
|
<!-- This has been written by hand. (c) 2010 Andreas Rumpf -->
|
||||||
<head>
|
<head>
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
<title>Nimrod ECMAScript Generator Test</title>
|
<title>Nimrod ECMAScript Generator Test</title>
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
span.DecNumber {color: blue}
|
span.DecNumber {color: blue}
|
||||||
</style>
|
</style>
|
||||||
<script src="nimrcache/ecmas.js" type="text/javascript"></script>
|
<script src="nimcache/ecmas.js" type="text/javascript"></script>
|
||||||
</head>
|
</head>
|
||||||
<body onload="OnLoad()">
|
<body onload="OnLoad()">
|
||||||
<form name="form1" action="ecmas.html">
|
<form name="form1" action="ecmas.html">
|
||||||
<input type="text" name="input" size="3" />
|
<input type="text" name="input1" size="10" />
|
||||||
<input type="button" value="Calculate square" onclick="OnButtonClick()" />
|
<input type="button" value="Calculate square" onclick="OnButtonClick()" />
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,14 @@ import
|
||||||
# We need to declare the used elements here. This is annoying but
|
# We need to declare the used elements here. This is annoying but
|
||||||
# prevents any kind of typo:
|
# prevents any kind of typo:
|
||||||
var
|
var
|
||||||
inputElement {.importc: "document.form1.input", nodecl.}: ref TElement
|
inputElement {.importc: "document.form1.input1", nodecl.}: ref TElement
|
||||||
|
|
||||||
proc OnButtonClick() {.exportc.} =
|
proc OnButtonClick() {.exportc.} =
|
||||||
var x: int = parseInt($inputElement.value)
|
#var x = parseInt($inputElement.value)
|
||||||
echo($(x * x))
|
#echo($(x * x))
|
||||||
|
var input = $inputElement.value
|
||||||
|
echo "Test"
|
||||||
|
echo "Hi, ", input
|
||||||
|
|
||||||
proc OnLoad() {.exportc.} =
|
proc OnLoad() {.exportc.} =
|
||||||
echo("Welcome! Please take your time to fill in this formular!")
|
echo "Welcome! Please take your time to fill in this formular!"
|
||||||
|
|
|
||||||
|
|
@ -808,11 +808,11 @@ void error1(TCCState *s1, int is_warning, const char *fmt, va_list ap)
|
||||||
if (!s1->error_func) {
|
if (!s1->error_func) {
|
||||||
/* default case: stderr */
|
/* default case: stderr */
|
||||||
fprintf(stderr, "%s\n", buf);
|
fprintf(stderr, "%s\n", buf);
|
||||||
} else {
|
|
||||||
s1->error_func(s1->error_opaque, buf);
|
|
||||||
}
|
}
|
||||||
if (!is_warning || s1->warn_error)
|
if (!is_warning || s1->warn_error) {
|
||||||
|
s1->error_func(s1->error_opaque, buf);
|
||||||
s1->nb_errors++;
|
s1->nb_errors++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void tcc_set_error_func(TCCState *s, void *error_opaque,
|
void tcc_set_error_func(TCCState *s, void *error_opaque,
|
||||||
|
|
|
||||||
2
todo.txt
2
todo.txt
|
|
@ -3,6 +3,8 @@ For version 0.8.10
|
||||||
|
|
||||||
- fix implicit generic routines
|
- fix implicit generic routines
|
||||||
- bugfix: ccgexprs
|
- bugfix: ccgexprs
|
||||||
|
- bugfix: make ``finally`` more robust
|
||||||
|
- bugfix: tiny C broken again!
|
||||||
|
|
||||||
|
|
||||||
High priority (version 0.9.0)
|
High priority (version 0.9.0)
|
||||||
|
|
|
||||||
|
|
@ -7,17 +7,38 @@
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
import glib2, gtk2, gdk2, osproc, dialogs, strutils
|
import glib2, gtk2, gdk2, os, osproc, dialogs, strutils
|
||||||
|
|
||||||
|
when defined(tinyc):
|
||||||
|
const runCmd = "run"
|
||||||
|
else:
|
||||||
|
const runCmd = "c -r"
|
||||||
|
|
||||||
|
when not defined(findExe):
|
||||||
|
# candidate for the stdlib:
|
||||||
|
proc findExe(exe: string): string =
|
||||||
|
## returns exe if the exe cannot be found
|
||||||
|
result = addFileExt(exe, os.exeExt)
|
||||||
|
if ExistsFile(result): return
|
||||||
|
var path = os.getEnv("PATH")
|
||||||
|
for candidate in split(path, pathSep):
|
||||||
|
var x = candidate / result
|
||||||
|
if ExistsFile(x): return x
|
||||||
|
result = ""
|
||||||
|
|
||||||
|
var nimExe = findExe("nimrod")
|
||||||
|
if nimExe.len == 0: nimExe = "../bin" / addFileExt("nimrod", os.exeExt)
|
||||||
|
|
||||||
proc execCode(code: string): string =
|
proc execCode(code: string): string =
|
||||||
var f: TFile
|
var f: TFile
|
||||||
if open(f, "temp.nim", fmWrite):
|
if open(f, "temp.nim", fmWrite):
|
||||||
f.write(code)
|
f.write(code)
|
||||||
f.close()
|
f.close()
|
||||||
else:
|
|
||||||
raise newException(EIO, "Unable to open file")
|
|
||||||
result = osproc.execProcess(
|
result = osproc.execProcess(
|
||||||
"nimrod c -r --verbosity:0 --hint[Conf]:off temp.nim")
|
"$# $# --verbosity:0 --hint[Conf]:off temp.nim" % [nimExe, runCmd],
|
||||||
|
{poStdErrToStdOut})
|
||||||
|
else:
|
||||||
|
result = "cannot open file 'temp.nim'"
|
||||||
|
|
||||||
var shiftPressed = False
|
var shiftPressed = False
|
||||||
var w: gtk2.PWindow
|
var w: gtk2.PWindow
|
||||||
|
|
@ -29,20 +50,17 @@ proc destroy(widget: PWidget, data: pgpointer){.cdecl.} =
|
||||||
|
|
||||||
proc FileOpenClicked(menuitem: PMenuItem, userdata: pgpointer) {.cdecl.} =
|
proc FileOpenClicked(menuitem: PMenuItem, userdata: pgpointer) {.cdecl.} =
|
||||||
var path = ChooseFileToOpen(w)
|
var path = ChooseFileToOpen(w)
|
||||||
|
|
||||||
if path != "":
|
if path != "":
|
||||||
|
|
||||||
var file: string = readFile(path)
|
var file: string = readFile(path)
|
||||||
if file != nil:
|
if file != nil:
|
||||||
set_text(InputTextBuffer, file, len(file))
|
set_text(InputTextBuffer, file, len(file))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
error(w, "Unable to read from file")
|
error(w, "Unable to read from file")
|
||||||
|
|
||||||
proc FileSaveClicked(menuitem: PMenuItem, userdata: pgpointer) {.cdecl.} =
|
proc FileSaveClicked(menuitem: PMenuItem, userdata: pgpointer) {.cdecl.} =
|
||||||
var path = ChooseFileToSave(w)
|
var path = ChooseFileToSave(w)
|
||||||
|
|
||||||
if path != "":
|
if path == "": return
|
||||||
var startIter: TTextIter
|
var startIter: TTextIter
|
||||||
var endIter: TTextIter
|
var endIter: TTextIter
|
||||||
get_start_iter(InputTextBuffer, addr(startIter))
|
get_start_iter(InputTextBuffer, addr(startIter))
|
||||||
|
|
@ -56,7 +74,6 @@ proc FileSaveClicked(menuitem: PMenuItem, userdata: pgpointer) {.cdecl.} =
|
||||||
else:
|
else:
|
||||||
error(w, "Unable to write to file")
|
error(w, "Unable to write to file")
|
||||||
|
|
||||||
|
|
||||||
proc inputKeyPressed(widget: PWidget, event: PEventKey,
|
proc inputKeyPressed(widget: PWidget, event: PEventKey,
|
||||||
userdata: pgpointer): bool =
|
userdata: pgpointer): bool =
|
||||||
if ($keyval_name(event.keyval)).tolower() == "shift_l":
|
if ($keyval_name(event.keyval)).tolower() == "shift_l":
|
||||||
|
|
@ -68,13 +85,13 @@ proc setError(msg: string) =
|
||||||
|
|
||||||
proc inputKeyReleased(widget: PWidget, event: PEventKey,
|
proc inputKeyReleased(widget: PWidget, event: PEventKey,
|
||||||
userdata: pgpointer): bool =
|
userdata: pgpointer): bool =
|
||||||
echo(keyval_name(event.keyval))
|
#echo(keyval_name(event.keyval))
|
||||||
if ($keyval_name(event.keyval)).tolower() == "shift_l":
|
if ($keyval_name(event.keyval)).tolower() == "shift_l":
|
||||||
# SHIFT is released
|
# SHIFT is released
|
||||||
shiftPressed = False
|
shiftPressed = False
|
||||||
|
|
||||||
if ($keyval_name(event.keyval)).tolower() == "return":
|
if ($keyval_name(event.keyval)).tolower() == "return":
|
||||||
echo($keyval_name(event.keyval), "Shift_L")
|
#echo($keyval_name(event.keyval), "Shift_L")
|
||||||
# Enter pressed
|
# Enter pressed
|
||||||
if shiftPressed == False:
|
if shiftPressed == False:
|
||||||
var startIter: TTextIter
|
var startIter: TTextIter
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue