Skip to content

Commit

Permalink
Move more builtins to DSL. Reduce embedded lib size.
Browse files Browse the repository at this point in the history
  • Loading branch information
fubark committed Sep 26, 2023
1 parent 58f9854 commit 82dba02
Show file tree
Hide file tree
Showing 12 changed files with 148 additions and 71 deletions.
4 changes: 4 additions & 0 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ pub fn build(b: *std.build.Builder) !void {
var opts = getDefaultOptions(target, optimize);
opts.ffi = false;
opts.malloc = .malloc;
opts.cli = false;
opts.applyOverrides();

var lib: *std.build.Step.Compile = undefined;
Expand Down Expand Up @@ -309,6 +310,7 @@ pub const Options = struct {
static: bool,
gc: bool,
ffi: bool,
cli: bool,

fn applyOverrides(self: *Options) void {
if (optMalloc) |malloc| {
Expand Down Expand Up @@ -347,6 +349,7 @@ fn getDefaultOptions(target: std.zig.CrossTarget, optimize: std.builtin.Optimize
.malloc = malloc,
.static = !target.getCpuArch().isWasm(),
.ffi = !target.getCpuArch().isWasm(),
.cli = !target.getCpuArch().isWasm(),
};
}

Expand Down Expand Up @@ -376,6 +379,7 @@ fn createBuildOptions(b: *std.build.Builder, opts: Options) !*std.build.Step.Opt
build_options.addOption(bool, "is32Bit", is32Bit(opts.target));
build_options.addOption(bool, "gc", opts.gc);
build_options.addOption(bool, "ffi", opts.ffi);
build_options.addOption(bool, "cli", opts.cli);
build_options.addOption([]const u8, "full_version", b.fmt("Cyber {s} build-{s}-{s}", .{Version, buildTag, commitTag}));
return build_options;
}
Expand Down
63 changes: 6 additions & 57 deletions src/builtins/bindings.zig
Original file line number Diff line number Diff line change
Expand Up @@ -184,14 +184,12 @@ pub fn bindCore(self: *cy.VM) linksection(cy.InitSection) !void {
self.pairIteratorMGID = try b.ensureMethodGroup("pairIterator");
const read = try b.ensureMethodGroup("read");
const readToEnd = try b.ensureMethodGroup("readToEnd");
const remove = try b.ensureMethodGroup("remove");
const repeat = try b.ensureMethodGroup("repeat");
const replace = try b.ensureMethodGroup("replace");
const runeAt = try b.ensureMethodGroup("runeAt");
const seek = try b.ensureMethodGroup("seek");
const seekFromCur = try b.ensureMethodGroup("seekFromCur");
const seekFromEnd = try b.ensureMethodGroup("seekFromEnd");
const size = try b.ensureMethodGroup("size");
const slice = try b.ensureMethodGroup("slice");
const sliceAt = try b.ensureMethodGroup("sliceAt");
const split = try b.ensureMethodGroup("split");
Expand All @@ -208,23 +206,18 @@ pub fn bindCore(self: *cy.VM) linksection(cy.InitSection) !void {
const write = try b.ensureMethodGroup("write");

// Init compile time builtins.
var rsym: sema.Symbol = undefined;
var sb: ModuleBuilder = undefined;

// Builtin types.
var id = try self.addBuiltinType("none", bt.None);
std.debug.assert(id == rt.NoneT);

id = try self.addBuiltinType("boolean", bt.Boolean);
std.debug.assert(id == rt.BooleanT);
var rsym = self.compiler.sema.getSymbol(bt.Boolean);
var sb = ModuleBuilder.init(self.compiler, rsym.inner.builtinType.modId);
try sb.setFunc("$call", &.{ bt.Any }, bt.Boolean, booleanCall);

id = try self.addBuiltinType("error", bt.Error);
std.debug.assert(id == rt.ErrorT);
rsym = self.compiler.sema.getSymbol(bt.Error);
sb = ModuleBuilder.init(self.compiler, rsym.inner.builtinType.modId);
try sb.setFunc("$call", &.{ bt.Any }, bt.Error, errorCall);
try b.addMethod(rt.ErrorT, value, &.{ bt.Any }, bt.Any, errorValue);

id = try self.addBuiltinType("StaticAstring", bt.String);
std.debug.assert(id == rt.StaticAstringT);
Expand All @@ -245,47 +238,9 @@ pub fn bindCore(self: *cy.VM) linksection(cy.InitSection) !void {

id = try self.addBuiltinType("integer", bt.Integer);
std.debug.assert(id == rt.IntegerT);
rsym = self.compiler.sema.getSymbol(bt.Integer);
sb = ModuleBuilder.init(self.compiler, rsym.inner.builtinType.modId);
try sb.setFunc("$call", &.{ bt.Any }, bt.Integer, integerCall);
try sb.addOptimizingMethod(rt.IntegerT, self.@"prefix~MGID", &.{ bt.Any }, bt.Integer, intBitwiseNot);
try sb.addOptimizingMethod(rt.IntegerT, self.@"prefix-MGID", &.{ bt.Any }, bt.Integer, intNeg);
// Inlined opcodes allow the right arg to be dynamic so the compiler can gen more of those.
// So for now, the runtime signature reflects that.
try sb.addOptimizingMethod(rt.IntegerT, self.@"infix<MGID", &.{ bt.Any, bt.Any }, bt.Boolean, inlineBinOp(.lessInt));
try sb.addOptimizingMethod(rt.IntegerT, self.@"infix<=MGID", &.{ bt.Any, bt.Any }, bt.Boolean, inlineBinOp(.lessEqualInt));
try sb.addOptimizingMethod(rt.IntegerT, self.@"infix>MGID", &.{ bt.Any, bt.Any }, bt.Boolean, inlineBinOp(.greaterInt));
try sb.addOptimizingMethod(rt.IntegerT, self.@"infix>=MGID", &.{ bt.Any, bt.Any }, bt.Boolean, inlineBinOp(.greaterEqualInt));
try sb.addOptimizingMethod(rt.IntegerT, self.@"infix+MGID", &.{ bt.Any, bt.Any }, bt.Integer, inlineBinOp(.addInt));
try sb.addOptimizingMethod(rt.IntegerT, self.@"infix+MGID", &.{ bt.Any, bt.Any }, bt.Integer, inlineBinOp(.addInt));
try sb.addOptimizingMethod(rt.IntegerT, self.@"infix+MGID", &.{ bt.Any, bt.Any }, bt.Integer, inlineBinOp(.addInt));
try sb.addOptimizingMethod(rt.IntegerT, self.@"infix-MGID", &.{ bt.Any, bt.Any }, bt.Integer, inlineBinOp(.subInt));
try sb.addOptimizingMethod(rt.IntegerT, self.@"infix*MGID", &.{ bt.Any, bt.Any }, bt.Integer, inlineBinOp(.mulInt));
try sb.addOptimizingMethod(rt.IntegerT, self.@"infix/MGID", &.{ bt.Any, bt.Any }, bt.Integer, inlineBinOp(.divInt));
try sb.addOptimizingMethod(rt.IntegerT, self.@"infix%MGID", &.{ bt.Any, bt.Any }, bt.Integer, inlineBinOp(.modInt));
try sb.addOptimizingMethod(rt.IntegerT, self.@"infix^MGID", &.{ bt.Any, bt.Any }, bt.Integer, inlineBinOp(.powInt));
try sb.addOptimizingMethod(rt.IntegerT, self.@"infix&MGID", &.{ bt.Any, bt.Any }, bt.Integer, inlineBinOp(.bitwiseAnd));
try sb.addOptimizingMethod(rt.IntegerT, self.@"infix|MGID", &.{ bt.Any, bt.Any }, bt.Integer, inlineBinOp(.bitwiseOr));
try sb.addOptimizingMethod(rt.IntegerT, self.@"infix||MGID", &.{ bt.Any, bt.Any }, bt.Integer, inlineBinOp(.bitwiseXor));
try sb.addOptimizingMethod(rt.IntegerT, self.@"infix<<MGID", &.{ bt.Any, bt.Any }, bt.Integer, inlineBinOp(.bitwiseLeftShift));
try sb.addOptimizingMethod(rt.IntegerT, self.@"infix>>MGID", &.{ bt.Any, bt.Any }, bt.Integer, inlineBinOp(.bitwiseRightShift));

id = try self.addBuiltinType("float", bt.Float);
std.debug.assert(id == rt.FloatT);
rsym = self.compiler.sema.getSymbol(bt.Float);
sb = ModuleBuilder.init(self.compiler, rsym.inner.builtinType.modId);
try sb.setFunc("$call", &.{ bt.Any }, bt.Float, floatCall);
try sb.addOptimizingMethod(rt.FloatT, self.@"infix<MGID", &.{ bt.Any, bt.Any }, bt.Boolean, inlineBinOp(.lessFloat));
try sb.addOptimizingMethod(rt.FloatT, self.@"infix<=MGID", &.{ bt.Any, bt.Any }, bt.Boolean, inlineBinOp(.lessEqualFloat));
try sb.addOptimizingMethod(rt.FloatT, self.@"infix>MGID", &.{ bt.Any, bt.Any }, bt.Boolean, inlineBinOp(.greaterFloat));
try sb.addOptimizingMethod(rt.FloatT, self.@"infix<=MGID", &.{ bt.Any, bt.Any }, bt.Boolean, inlineBinOp(.greaterEqualFloat));
try sb.addOptimizingMethod(rt.FloatT, self.@"prefix-MGID", &.{ bt.Any }, bt.Float, floatNeg);
try sb.addOptimizingMethod(rt.FloatT, self.@"infix+MGID", &.{ bt.Any, bt.Any }, bt.Float, inlineBinOp(.addFloat));
try sb.addOptimizingMethod(rt.FloatT, self.@"infix-MGID", &.{ bt.Any, bt.Any }, bt.Float, inlineBinOp(.subFloat));
try sb.addOptimizingMethod(rt.FloatT, self.@"infix*MGID", &.{ bt.Any, bt.Any }, bt.Float, inlineBinOp(.mulFloat));
try sb.addOptimizingMethod(rt.FloatT, self.@"infix/MGID", &.{ bt.Any, bt.Any }, bt.Float, inlineBinOp(.divFloat));
try sb.addOptimizingMethod(rt.FloatT, self.@"infix%MGID", &.{ bt.Any, bt.Any }, bt.Float, inlineBinOp(.modFloat));
try sb.addOptimizingMethod(rt.FloatT, self.@"infix^MGID", &.{ bt.Any, bt.Any }, bt.Float, inlineBinOp(.powFloat));

id = try self.addBuiltinType("List", bt.List);
std.debug.assert(id == rt.ListT);
Expand All @@ -297,12 +252,6 @@ pub fn bindCore(self: *cy.VM) linksection(cy.InitSection) !void {

id = try self.addBuiltinType("Map", bt.Map);
std.debug.assert(id == rt.MapT);
try b.addOptimizingMethod(rt.MapT, self.indexMGID, &.{ bt.Any, bt.Any }, bt.Any, inlineBinOp(.indexMap));
try b.addOptimizingMethod(rt.MapT, self.setIndexMGID, &.{ bt.Any, bt.Any, bt.Any }, bt.None, inlineTernNoRetOp(.setIndexMap));
try b.addMethod(rt.MapT, remove, &.{ bt.Any, bt.Any }, bt.None, mapRemove);
try b.addMethod(rt.MapT, size, &.{ bt.Any }, bt.Integer, mapSize);
try b.addMethod(rt.MapT, self.iteratorMGID, &.{ bt.Any }, bt.Any, mapIterator);
try b.addMethod(rt.MapT, self.pairIteratorMGID, &.{ bt.Any }, bt.Any, mapIterator);

id = try self.addBuiltinType("MapIterator", cy.NullId);
std.debug.assert(id == rt.MapIteratorT);
Expand Down Expand Up @@ -766,7 +715,7 @@ pub fn listResize(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection(cy.St
return Value.None;
}

fn mapIterator(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection(Section) Value {
pub fn mapIterator(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection(Section) Value {
const obj = args[0].asHeapObject();
vm.retainObject(obj);
return vm.allocMapIterator(&obj.map) catch fatal();
Expand Down Expand Up @@ -797,13 +746,13 @@ fn mapIteratorNext(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection(cy.S
} else return Value.None;
}

fn mapSize(_: *cy.UserVM, args: [*]const Value, _: u8) linksection(cy.Section) Value {
pub fn mapSize(_: *cy.UserVM, args: [*]const Value, _: u8) linksection(cy.Section) Value {
const obj = args[0].asHeapObject();
const inner = cy.ptrAlignCast(*cy.MapInner, &obj.map.inner);
return Value.initInt(@intCast(inner.size));
}

fn mapRemove(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection(cy.StdSection) Value {
pub fn mapRemove(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection(cy.StdSection) Value {
const obj = args[0].asHeapObject();
const inner = cy.ptrAlignCast(*cy.MapInner, &obj.map.inner);
_ = inner.remove(@ptrCast(vm), args[1]);
Expand Down Expand Up @@ -841,7 +790,7 @@ pub fn listLen(_: *cy.UserVM, args: [*]const Value, _: u8) linksection(cy.Sectio
// return Value.None;
// }

fn errorValue(vm: *cy.UserVM, args: [*]const Value, _: u8) Value {
pub fn errorValue(vm: *cy.UserVM, args: [*]const Value, _: u8) Value {
const recv = args[0];
const enumId = (recv.val & 0xFF00) >> 8;
if (enumId == cy.NullU8) {
Expand Down
54 changes: 54 additions & 0 deletions src/builtins/builtins.cy
Original file line number Diff line number Diff line change
@@ -1,3 +1,48 @@
@host
type boolean object:
@host func '$call'(val any) boolean

@host
type 'error' object:
@host func '$call'(val any) error
@host func value(self) any

@host
type int object:
@host func '$call'(val any) int
@host func '$prefix~'(self) int
@host func '$prefix-'(self) int
@host func '$infix<'(self, o any) boolean
@host func '$infix<='(self, o any) boolean
@host func '$infix>'(self, o any) boolean
@host func '$infix>='(self, o any) boolean
@host func '$infix+'(self, o any) int
@host func '$infix-'(self, o any) int
@host func '$infix*'(self, o any) int
@host func '$infix/'(self, o any) int
@host func '$infix%'(self, o any) int
@host func '$infix^'(self, o any) int
@host func '$infix&'(self, o any) int
@host func '$infix|'(self, o any) int
@host func '$infix||'(self, o any) int
@host func '$infix<<'(self, o any) int
@host func '$infix>>'(self, o any) int

@host
type float object:
@host func '$call'(val any) float
@host func '$prefix-'(self) float
@host func '$infix<'(self, o any) boolean
@host func '$infix<='(self, o any) boolean
@host func '$infix>'(self, o any) boolean
@host func '$infix>='(self, o any) boolean
@host func '$infix+'(self, o any) float
@host func '$infix-'(self, o any) float
@host func '$infix*'(self, o any) float
@host func '$infix/'(self, o any) float
@host func '$infix%'(self, o any) float
@host func '$infix^'(self, o any) float

@host
type List object:
@host func '$index'(self, idx any) any
Expand All @@ -14,6 +59,15 @@ type List object:
@host func resize(self, size int) any
@host func sort(self, lessFn any) any

@host
type Map object:
@host func '$index'(self, key any) any
@host func '$setIndex'(self, key any, val any) none
@host func remove(self, key any) none
@host func size(self) int
@host func iterator(self) any
@host func pairIterator(self) any

-- type string trait:
-- func append(self, str any) string
-- func charAt(self, idx int) any
Expand Down
56 changes: 56 additions & 0 deletions src/builtins/builtins.zig
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,49 @@ pub fn postLoad(vm: *cy.UserVM, modId: cy.ModuleId) callconv(.C) void {

const NameFunc = struct { []const u8, ?*const anyopaque, cy.HostFuncType };
const funcs = [_]NameFunc{
// boolean
.{"$call", bindings.booleanCall, .standard},

// error
.{"$call", bindings.errorCall, .standard},
.{"value", bindings.errorValue, .standard},

// int
.{"$call", bindings.integerCall, .standard},
.{"$prefix~", bindings.intBitwiseNot, .quicken},
.{"$prefix-", bindings.intNeg, .quicken},
// Inlined opcodes allow the right arg to be dynamic so the compiler can gen more of those.
// So for now, the runtime signature reflects that.
.{"$infix<", bindings.inlineBinOp(.lessInt), .quicken},
.{"$infix<=", bindings.inlineBinOp(.lessEqualInt), .quicken},
.{"$infix>", bindings.inlineBinOp(.greaterInt), .quicken},
.{"$infix>=", bindings.inlineBinOp(.greaterEqualInt), .quicken},
.{"$infix+", bindings.inlineBinOp(.addInt), .quicken},
.{"$infix-", bindings.inlineBinOp(.subInt), .quicken},
.{"$infix*", bindings.inlineBinOp(.mulInt), .quicken},
.{"$infix/", bindings.inlineBinOp(.divInt), .quicken},
.{"$infix%", bindings.inlineBinOp(.modInt), .quicken},
.{"$infix^", bindings.inlineBinOp(.powInt), .quicken},
.{"$infix&", bindings.inlineBinOp(.bitwiseAnd), .quicken},
.{"$infix|", bindings.inlineBinOp(.bitwiseOr), .quicken},
.{"$infix||", bindings.inlineBinOp(.bitwiseXor), .quicken},
.{"$infix<<", bindings.inlineBinOp(.bitwiseLeftShift), .quicken},
.{"$infix>>", bindings.inlineBinOp(.bitwiseRightShift), .quicken},

// float
.{"$call", bindings.floatCall, .standard},
.{"$prefix-", bindings.floatNeg, .quicken},
.{"$infix<", bindings.inlineBinOp(.lessFloat), .quicken},
.{"$infix<=", bindings.inlineBinOp(.lessEqualFloat), .quicken},
.{"$infix>", bindings.inlineBinOp(.greaterFloat), .quicken},
.{"$infix>=", bindings.inlineBinOp(.greaterEqualFloat), .quicken},
.{"$infix+", bindings.inlineBinOp(.addFloat), .quicken},
.{"$infix-", bindings.inlineBinOp(.subFloat), .quicken},
.{"$infix*", bindings.inlineBinOp(.mulFloat), .quicken},
.{"$infix/", bindings.inlineBinOp(.divFloat), .quicken},
.{"$infix%", bindings.inlineBinOp(.modFloat), .quicken},
.{"$infix^", bindings.inlineBinOp(.powFloat), .quicken},

// List
.{"$index", bindings.inlineBinOp(.indexList), .quicken},
.{"$setIndex", bindings.inlineTernNoRetOp(.setIndexList), .quicken},
Expand All @@ -49,6 +92,14 @@ const funcs = [_]NameFunc{
.{"resize", bindings.listResize, .standard},
.{"sort", bindings.listSort, .standard},

// Map
.{"$index", bindings.inlineBinOp(.indexMap), .quicken},
.{"$setIndex", bindings.inlineTernNoRetOp(.setIndexMap), .quicken},
.{"remove", bindings.mapRemove, .standard},
.{"size", bindings.mapSize, .standard},
.{"iterator", bindings.mapIterator, .standard},
.{"pairIterator", bindings.mapIterator, .standard},

// Utils.
.{"arrayFill", arrayFill, .standard},
.{"asciiCode", asciiCode, .standard},
Expand All @@ -75,7 +126,12 @@ const funcs = [_]NameFunc{

const NameType = struct { []const u8, cy.rt.TypeId, cy.types.TypeId };
const types = [_]NameType{
.{"boolean", rt.BooleanT, bt.Boolean },
.{"error", rt.ErrorT, bt.Error },
.{"int", rt.IntegerT, bt.Integer },
.{"float", rt.FloatT, bt.Float },
.{"List", rt.ListT, bt.List },
.{"Map", rt.MapT, bt.Map },
};

pub fn typeLoader(_: *cy.UserVM, info: cy.HostTypeInfo, out: *cy.HostTypeResult) callconv(.C) bool {
Expand Down
10 changes: 7 additions & 3 deletions src/codegen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1233,8 +1233,10 @@ fn statement(c: *Chunk, nodeId: cy.NodeId) !void {
const param = c.nodes[decl.paramHead];
const paramName = c.getNodeTokenString(c.nodes[param.head.funcParam.name]);
if (std.mem.eql(u8, paramName, "self")) {
// Struct method.
try genMethodDecl(c, sid, func, decl, funcName);
// Object method.
if (func.node_t == .funcDecl) {
try genMethodDecl(c, sid, func, decl, funcName);
}
continue;
}
}
Expand All @@ -1243,7 +1245,9 @@ fn statement(c: *Chunk, nodeId: cy.NodeId) !void {
// .name = try c.alloc.dupe(u8, funcName),
// };
// try c.compiler.vm.funcSymDetails.append(c.alloc, detail);
try funcDecl(c, robjSymId, funcId);
if (func.node_t == .funcDecl) {
try funcDecl(c, robjSymId, funcId);
}
}
},
.if_stmt => {
Expand Down
1 change: 1 addition & 0 deletions src/cyber.zig
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ pub const is32Bit = build_options.is32Bit;
pub const hasStdFiles = !isWasm;
pub const hasGC = build_options.gc;
pub const hasFFI = build_options.ffi;
pub const hasCLI = build_options.cli;

const build_options = @import("build_options");
pub const Trace = build_options.trace;
Expand Down
4 changes: 2 additions & 2 deletions src/http.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ const std = @import("std");
const stdx = @import("stdx");
const cy = @import("cyber.zig");

const Request = if (!cy.isWasm) std.http.Client.Request else void;
const StdResponse = if (!cy.isWasm) std.http.Client.Response else void;
const Request = if (cy.hasCLI) std.http.Client.Request else void;
const StdResponse = if (cy.hasCLI) std.http.Client.Response else void;

/// Interface to http client.
pub const HttpClient = struct {
Expand Down
4 changes: 3 additions & 1 deletion src/include/cyber.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,9 +162,11 @@ typedef enum {
HOST_TYPE_CORE_OBJECT,
} HostTypeType;

// If an object's size in bytes is at or below this maximum,
// it is automatically managed by Cyber's object pool.
#define CS_MAX_POOL_OBJECT_SIZE 32

// If objects allocated for the binded type ever exceeds `CS_MAX_POOL_OBJECT_SIZE`,
// If the memory occupied by an object ever exceeds `CS_MAX_POOL_OBJECT_SIZE`,
// then a finalizer is required to explicitly free the memory with `csFree`.
// A finalizer can also be used to perform cleanup tasks. eg. Freeing resource handles.
// Unlike finalizers declared in user scripts, this finalizer is always guaranteed to be invoked.
Expand Down
6 changes: 5 additions & 1 deletion src/parser.zig
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,10 @@ pub const Parser = struct {
const id = try self.pushIdentNode(self.next_pos);
self.advanceToken();
return id;
} else if (token.tag() == .error_k) {
const id = try self.pushIdentNode(self.next_pos);
self.advanceToken();
return id;
}
return null;
}
Expand Down Expand Up @@ -636,7 +640,7 @@ pub const Parser = struct {

// Parse name.
var token = self.peekToken();
if (token.tag() != .ident) {
if (token.tag() != .ident and token.tag() != .string) {
return self.reportParseError("Expected type name identifier.", &.{});
}
const name = try self.pushIdentNode(self.next_pos);
Expand Down
2 changes: 1 addition & 1 deletion src/sema.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1118,7 +1118,7 @@ fn declareHostMethod(c: *cy.Chunk, modId: cy.ModuleId, nodeId: cy.NodeId) !void
// Insert method entries into VM.
const funcSig = c.compiler.sema.getFuncSig(func.funcSigId);
if (sym.symT == .hostFunc) {
if (funcSig.isTyped) {
if (funcSig.isParamsTyped) {
const m = rt.MethodInit.initHostTyped(func.funcSigId, @ptrCast(sym.inner.hostFunc.func), func.numParams);
try c.compiler.vm.addMethod(typeId, mgId, m);
} else {
Expand Down
Loading

0 comments on commit 82dba02

Please sign in to comment.