1
0
mirror of https://github.com/fluencelabs/assemblyscript synced 2025-05-07 04:42:15 +00:00

Initial element access compilation; Carefully approaching std array

This commit is contained in:
dcodeIO 2018-01-13 23:38:07 +01:00
parent dd596b015d
commit 2c009c67d3
21 changed files with 9914 additions and 88 deletions

@ -363,12 +363,12 @@ export abstract class Node {
stmt.decoratorKind = DecoratorKind.OPERATOR;
break;
case "struct":
stmt.decoratorKind = DecoratorKind.STRUCT;
case "explicit":
stmt.decoratorKind = DecoratorKind.EXPLICIT;
break;
case "size":
stmt.decoratorKind = DecoratorKind.SIZE;
case "offset":
stmt.decoratorKind = DecoratorKind.OFFSET;
break;
default:
@ -1382,8 +1382,8 @@ export const enum DecoratorKind {
CUSTOM,
GLOBAL,
OPERATOR,
STRUCT,
SIZE
EXPLICIT,
OFFSET
}
/** Depresents a decorator. */

@ -1349,7 +1349,7 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty
}
arg0 = compiler.compileExpression(operands[0], usizeType);
compiler.currentType = typeArguments[0];
return module.createLoad(typeArguments[0].size >>> 3, typeArguments[0].is(TypeFlags.SIGNED | TypeFlags.INTEGER), arg0, typeArguments[0].toNativeType());
return module.createLoad(typeArguments[0].byteSize, typeArguments[0].is(TypeFlags.SIGNED | TypeFlags.INTEGER), arg0, typeArguments[0].toNativeType());
case "store": // store<T?>(offset: usize, value: T) -> void
compiler.currentType = Type.void;
@ -1372,7 +1372,7 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty
}
type = compiler.currentType;
compiler.currentType = Type.void;
return module.createStore(type.size >>> 3, arg0, arg1, type.toNativeType());
return module.createStore(type.byteSize, arg0, arg1, type.toNativeType());
case "sizeof": // sizeof<T!>() -> usize
compiler.currentType = usizeType;

@ -2217,6 +2217,16 @@ export class Compiler extends DiagnosticEmitter {
this.error(DiagnosticCode.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property, expression.range, (<Property>element).internalName);
return this.module.createUnreachable();
case ElementKind.FUNCTION_PROTOTYPE:
if (expression.kind == NodeKind.ELEMENTACCESS) { // @operator("[]")
assert(resolved.target && resolved.target.kind == ElementKind.CLASS && element.simpleName == (<Class>resolved.target).prototype.fnIndexedGet)
var resolvedIndexedSet = (<FunctionPrototype>element).resolve(null);
if (resolvedIndexedSet) {
elementType = resolvedIndexedSet.returnType;
break;
}
}
// fall-through
default:
this.error(DiagnosticCode.Operation_not_supported, expression.range);
return this.module.createUnreachable();
@ -2275,11 +2285,11 @@ export class Compiler extends DiagnosticEmitter {
this.currentType = tee ? (<Field>element).type : Type.void;
var elementNativeType = (<Field>element).type.toNativeType();
if (!tee)
return this.module.createStore((<Field>element).type.byteSize, targetExpr, valueWithCorrectType, elementNativeType, (<Field>element).memoryOffset);
return this.module.createStore((<Field>element).type.size >> 3, targetExpr, valueWithCorrectType, elementNativeType, (<Field>element).memoryOffset);
tempLocal = this.currentFunction.getAndFreeTempLocal((<Field>element).type);
return this.module.createBlock(null, [ // TODO: simplify if valueWithCorrectType has no side effects
this.module.createSetLocal(tempLocal.index, valueWithCorrectType),
this.module.createStore((<Field>element).type.byteSize, targetExpr, this.module.createGetLocal(tempLocal.index, elementNativeType), elementNativeType, (<Field>element).memoryOffset),
this.module.createStore((<Field>element).type.size >> 3, targetExpr, this.module.createGetLocal(tempLocal.index, elementNativeType), elementNativeType, (<Field>element).memoryOffset),
this.module.createGetLocal(tempLocal.index, elementNativeType)
], elementNativeType);
@ -2325,6 +2335,38 @@ export class Compiler extends DiagnosticEmitter {
} else
this.error(DiagnosticCode.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property, expression.range, (<Property>element).internalName);
return this.module.createUnreachable();
case ElementKind.FUNCTION_PROTOTYPE:
if (expression.kind == NodeKind.ELEMENTACCESS) { // @operator("[]")
assert(resolved.target && resolved.target.kind == ElementKind.CLASS);
var resolvedIndexedGet = (<FunctionPrototype>element).resolve();
if (!resolvedIndexedGet)
return this.module.createUnreachable();
var indexedSetName = (<Class>resolved.target).prototype.fnIndexedSet;
var indexedSet: Element | null;
if (indexedSetName != null && (<Class>resolved.target).members && (indexedSet = (<Map<string,Element>>(<Class>resolved.target).members).get(indexedSetName)) && indexedSet.kind == ElementKind.FUNCTION_PROTOTYPE) { // @operator("[]=")
var resolvedIndexedSet = (<FunctionPrototype>indexedSet).resolve();
if (!resolvedIndexedSet)
return this.module.createUnreachable();
targetExpr = this.compileExpression(<Expression>resolved.targetExpression, this.options.target == Target.WASM64 ? Type.usize64 : Type.usize32, ConversionKind.NONE);
assert(this.currentType.classType);
var elementExpr = this.compileExpression((<ElementAccessExpression>expression).elementExpression, Type.i32);
if (!tee) {
this.currentType = resolvedIndexedSet.returnType;
return this.makeCall(resolvedIndexedSet, [ targetExpr, elementExpr, valueWithCorrectType ]);
}
this.currentType = resolvedIndexedGet.returnType;
tempLocal = this.currentFunction.getAndFreeTempLocal(this.currentType);
return this.module.createBlock(null, [
this.makeCall(resolvedIndexedSet, [ targetExpr, elementExpr, this.module.createTeeLocal(tempLocal.index, valueWithCorrectType) ]),
this.module.createGetLocal(tempLocal.index, tempLocal.type.toNativeType()) // TODO: could be different from an actual __get (needs 2 temp locals)
], this.currentType.toNativeType());
} else {
this.error(DiagnosticCode.Index_signature_in_type_0_only_permits_reading, expression.range, (<Class>resolved.target).internalName);
return this.module.createUnreachable();
}
}
// fall-through
}
this.error(DiagnosticCode.Operation_not_supported, expression.range);
return this.module.createUnreachable();
@ -2465,8 +2507,11 @@ export class Compiler extends DiagnosticEmitter {
var resolved = this.program.resolveElementAccess(expression, this.currentFunction); // reports
if (!resolved)
return this.module.createUnreachable();
throw new Error("not implemented"); // TODO
assert(resolved.element.kind == ElementKind.FUNCTION_PROTOTYPE && resolved.target && resolved.target.kind == ElementKind.CLASS);
var instance = (<FunctionPrototype>resolved.element).resolve(null, (<Class>resolved.target).contextualTypeArguments);
if (!instance)
return this.module.createUnreachable();
return this.compileCall(instance, [ expression.expression, expression.elementExpression ], expression);
}
compileIdentifierExpression(expression: IdentifierExpression, contextualType: Type): ExpressionRef {
@ -2632,7 +2677,7 @@ export class Compiler extends DiagnosticEmitter {
assert((<Field>element).memoryOffset >= 0);
targetExpr = this.compileExpression(<Expression>resolved.targetExpression, this.options.target == Target.WASM64 ? Type.usize64 : Type.usize32);
this.currentType = (<Field>element).type;
return this.module.createLoad((<Field>element).type.byteSize, (<Field>element).type.is(TypeFlags.SIGNED | TypeFlags.INTEGER),
return this.module.createLoad((<Field>element).type.size >> 3, (<Field>element).type.is(TypeFlags.SIGNED | TypeFlags.INTEGER),
targetExpr,
(<Field>element).type.toNativeType(),
(<Field>element).memoryOffset

@ -1,8 +1,14 @@
// internal naming scheme
/** Path delimited inserted between file system levels. */
export const PATH_DELIMITER = "/";
/** Substitution used to indicate the parent directory. */
export const PARENT_SUBST = "..";
/** Function name prefix used for getters. */
export const GETTER_PREFIX = "get:";
/** Function name prefix used for setters. */
export const SETTER_PREFIX = "set:";
/** Delimiter used between class names and instance members. */
export const INSTANCE_DELIMITER = "#";
/** Delimited used between class and namespace names and static members. */
export const STATIC_DELIMITER = ".";

@ -79,6 +79,7 @@ export enum DiagnosticCode {
Export_declaration_conflicts_with_exported_declaration_of_0 = 2484,
Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property = 2540,
The_target_of_an_assignment_must_be_a_variable_or_a_property_access = 2541,
Index_signature_in_type_0_only_permits_reading = 2542,
Expected_0_arguments_but_got_1 = 2554,
Expected_at_least_0_arguments_but_got_1 = 2555,
Expected_0_type_arguments_but_got_1 = 2558,
@ -166,6 +167,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
case 2484: return "Export declaration conflicts with exported declaration of '{0}'.";
case 2540: return "Cannot assign to '{0}' because it is a constant or a read-only property.";
case 2541: return "The target of an assignment must be a variable or a property access.";
case 2542: return "Index signature in type '{0}' only permits reading.";
case 2554: return "Expected {0} arguments, but got {1}.";
case 2555: return "Expected at least {0} arguments, but got {1}.";
case 2558: return "Expected {0} type arguments, but got {1}.";

@ -79,6 +79,7 @@
"Export declaration conflicts with exported declaration of '{0}'.": 2484,
"Cannot assign to '{0}' because it is a constant or a read-only property.": 2540,
"The target of an assignment must be a variable or a property access.": 2541,
"Index signature in type '{0}' only permits reading.": 2542,
"Expected {0} arguments, but got {1}.": 2554,
"Expected at least {0} arguments, but got {1}.": 2555,
"Expected {0} type arguments, but got {1}.": 2558,

@ -1580,6 +1580,19 @@ export class Parser extends DiagnosticEmitter {
var startPos = expr.range.start;
// ElementAccessExpression
if (tn.skip(Token.OPENBRACKET)) {
next = this.parseExpression(tn); // resets precedence
if (!next)
return null;
if (tn.skip(Token.CLOSEBRACKET))
expr = Node.createElementAccessExpression(<Expression>expr, <Expression>next, tn.range(startPos, tn.pos));
else {
this.error(DiagnosticCode._0_expected, tn.range(), "]");
return null;
}
}
// CallExpression
var typeArguments = this.tryParseTypeArgumentsBeforeArguments(tn); // skips '(' on success
// there might be better ways to distinguish a LESSTHAN from a CALL with type arguments
@ -1593,7 +1606,6 @@ export class Parser extends DiagnosticEmitter {
var token: Token;
var next: Expression | null = null;
var nextPrecedence: Precedence;
while ((nextPrecedence = determinePrecedence(token = tn.peek())) >= precedence) { // precedence climbing
tn.next();
@ -1604,19 +1616,6 @@ export class Parser extends DiagnosticEmitter {
return null;
expr = Node.createAssertionExpression(AssertionKind.AS, expr, toType, tn.range(startPos, tn.pos));
// ElementAccessExpression
} else if (token == Token.OPENBRACKET) {
next = this.parseExpression(tn); // resets precedence
if (!next)
return null;
if (tn.skip(Token.CLOSEBRACKET))
expr = Node.createElementAccessExpression(<Expression>expr, <Expression>next, tn.range(startPos, tn.pos));
else {
this.error(DiagnosticCode._0_expected, tn.range(), "]");
return null;
}
// UnaryPostfixExpression
} else if (token == Token.PLUS_PLUS || token == Token.MINUS_MINUS) {
if (expr.kind != NodeKind.IDENTIFIER && expr.kind != NodeKind.ELEMENTACCESS && expr.kind != NodeKind.PROPERTYACCESS)

@ -290,8 +290,8 @@ export class Program extends DiagnosticEmitter {
this.checkGlobalAlias(prototype, declaration);
if (hasDecorator("struct", declaration.decorators)) {
prototype.isStruct = true;
if (hasDecorator("explicit", declaration.decorators)) {
prototype.isExplicit = true;
if (declaration.implementsTypes && declaration.implementsTypes.length)
this.error(DiagnosticCode.Structs_cannot_implement_interfaces, Range.join(declaration.name.range, declaration.implementsTypes[declaration.implementsTypes.length - 1].range));
} else if (declaration.implementsTypes.length)
@ -413,8 +413,8 @@ export class Program extends DiagnosticEmitter {
} else
classPrototype.instanceMembers = new Map();
instancePrototype = new FunctionPrototype(this, name, internalName, declaration, classPrototype);
// if (classPrototype.isStruct && instancePrototype.isAbstract) {
// this.error( Structs cannot declare abstract methods. );
// if (classPrototype.isExplicit && instancePrototype.isAbstract) {
// this.error( Explicit classes cannot declare abstract methods. );
// }
classPrototype.instanceMembers.set(name, instancePrototype);
}
@ -436,19 +436,19 @@ export class Program extends DiagnosticEmitter {
switch ((<StringLiteralExpression>firstArg).value) {
case "[]":
classPrototype.opIndexedGet = instancePrototype;
classPrototype.fnIndexedGet = instancePrototype.simpleName;
break;
case "[]=":
classPrototype.opIndexedSet = instancePrototype;
classPrototype.fnIndexedSet = instancePrototype.simpleName;
break;
case "+":
classPrototype.opConcat = instancePrototype;
classPrototype.fnConcat = instancePrototype.simpleName;
break;
case "==":
classPrototype.opEquals = instancePrototype;
classPrototype.fnEquals = instancePrototype.simpleName;
break;
default: // TBD: does it make sense to provide more, even though not JS/TS-compatible?
@ -1078,13 +1078,15 @@ export class Program extends DiagnosticEmitter {
var target = resolvedElement.element;
switch (target.kind) {
// TBD: should indexed access on static classes, like `Heap`, be a supported as well?
case ElementKind.CLASS:
var type = (<Class>target).type;
case ElementKind.GLOBAL:
case ElementKind.LOCAL:
case ElementKind.FIELD:
var type = (<VariableLikeElement>target).type;
if (type.classType) {
var indexedGet: FunctionPrototype | null;
if (indexedGet = (target = type.classType).prototype.opIndexedGet)
return resolvedElement.set(indexedGet).withTarget(target, targetExpression);
var indexedGetName = (target = type.classType).prototype.fnIndexedGet;
var indexedGet: Element | null;
if (indexedGetName != null && target.members && (indexedGet = target.members.get(indexedGetName)) && indexedGet.kind == ElementKind.FUNCTION_PROTOTYPE)
return resolvedElement.set(indexedGet).withTarget(type.classType, targetExpression);
}
break;
}
@ -1221,8 +1223,8 @@ export enum ElementFlags {
PRIVATE = 1 << 15,
/** Is an abstract member. */
ABSTRACT = 1 << 16,
/** Is a struct-like class with limited capabilites. */
STRUCT = 1 << 17,
/** Is an explicitly layed out and allocated class with limited capabilites. */
EXPLICIT = 1 << 17,
/** Has already inherited base class static members. */
HAS_STATIC_BASE_MEMBERS = 1 << 18
}
@ -1877,13 +1879,13 @@ export class ClassPrototype extends Element {
basePrototype: ClassPrototype | null = null; // set in Program#initialize
/** Overloaded indexed get method, if any. */
opIndexedGet: FunctionPrototype | null = null; // TODO: indexedGet and indexedSet as an accessor?
fnIndexedGet: string | null = null;
/** Overloaded indexed set method, if any. */
opIndexedSet: FunctionPrototype | null = null;
fnIndexedSet: string | null = null;
/** Overloaded concatenation method, if any. */
opConcat: FunctionPrototype | null = null;
fnConcat: string | null = null;
/** Overloaded equality comparison method, if any. */
opEquals: FunctionPrototype | null = null;
fnEquals: string | null = null;
constructor(program: Program, simpleName: string, internalName: string, declaration: ClassDeclaration | null = null) {
super(program, simpleName, internalName);
@ -1903,9 +1905,9 @@ export class ClassPrototype extends Element {
}
}
/** Whether a struct-like class with limited capabilities or not. */
get isStruct(): bool { return (this.flags & ElementFlags.STRUCT) != 0; }
set isStruct(is: bool) { if (is) this.flags |= ElementFlags.STRUCT; else this.flags &= ~ElementFlags.STRUCT; }
/** Whether explicitly layed out and allocated */
get isExplicit(): bool { return (this.flags & ElementFlags.EXPLICIT) != 0; }
set isExplicit(is: bool) { if (is) this.flags |= ElementFlags.EXPLICIT; else this.flags &= ~ElementFlags.EXPLICIT; }
resolve(typeArguments: Type[] | null, contextualTypeArguments: Map<string,Type> | null = null): Class | null {
var instanceKey = typeArguments ? typesToString(typeArguments) : "";
@ -1933,7 +1935,7 @@ export class ClassPrototype extends Element {
this.program.error(DiagnosticCode.A_class_may_only_extend_another_class, declaration.extendsType.range);
return null;
}
if (baseClass.prototype.isStruct != this.isStruct) {
if (baseClass.prototype.isExplicit != this.isExplicit) {
this.program.error(DiagnosticCode.Structs_cannot_extend_classes_and_vice_versa, Range.join(declaration.name.range, declaration.extendsType.range));
return null;
}
@ -1957,9 +1959,9 @@ export class ClassPrototype extends Element {
instance.contextualTypeArguments = contextualTypeArguments;
this.instances.set(instanceKey, instance);
var memoryOffset: i32 = 0;
var memoryOffset: u32 = 0;
if (baseClass) {
memoryOffset = baseClass.type.byteSize;
memoryOffset = baseClass.currentMemoryOffset;
if (baseClass.members) {
if (!instance.members)
instance.members = new Map();
@ -1983,7 +1985,7 @@ export class ClassPrototype extends Element {
var fieldType = this.program.resolveType(fieldDeclaration.type, instance.contextualTypeArguments); // reports
if (fieldType) {
var fieldInstance = new Field(<FieldPrototype>member, (<FieldPrototype>member).internalName, fieldType);
switch (fieldType.size >> 3) { // align (byteSize might vary if a class type)
switch (fieldType.byteSize) { // align
case 1: break;
case 2: if (memoryOffset & 1) ++memoryOffset; break;
case 4: if (memoryOffset & 3) memoryOffset = (memoryOffset | 3) + 1; break;
@ -2015,7 +2017,7 @@ export class ClassPrototype extends Element {
}
}
instance.type.byteSize = memoryOffset; // sizeof<this>() is its byte size in memory
instance.currentMemoryOffset = memoryOffset; // sizeof<this>() is its byte size in memory
return instance;
}
@ -2051,6 +2053,8 @@ export class Class extends Element {
base: Class | null;
/** Contextual type arguments for fields and methods. */
contextualTypeArguments: Map<string,Type> | null = null;
/** Current member memory offset. */
currentMemoryOffset: u32 = 0;
/** Constructs a new class. */
constructor(prototype: ClassPrototype, internalName: string, typeArguments: Type[] | null = null, base: Class | null = null) {

@ -76,8 +76,8 @@ export class Type {
/** Type flags. */
flags: TypeFlags;
/** Size in bits. */
size: i32;
/** Size in bytes. */
size: u32;
/** Size in bytes. Ceiled to 8-bits. */
byteSize: i32;
/** Underlying class type, if a class type. */
classType: Class | null;

19
std/assembly.d.ts vendored

@ -202,6 +202,12 @@ declare class Array<T> {
constructor(capacity?: i32);
}
/** Class representing a C-like array of values of type `T` with limited capabilities. */
declare class CArray<T> {
[key: number]: T;
private constructor();
}
/** Class representing a sequence of characters. */
declare class String {
@ -281,13 +287,16 @@ interface Number {}
interface Object {}
interface RegExp {}
// Internal decorators (not yet implemented)
// Internal decorators
/** Annotates an element being part of the global namespace. */
/** Annotates an element as a program global. */
declare function global(target: Function): any;
/** Annotates a method being an operator overload. */
/** Annotates a method as an operator overload. */
declare function operator(token: string): any;
declare function struct(target: Function): any;
declare function size(size: usize): any;
/** Annotates a class as explicitly layed out and allocated. */
declare function explicit(target: Function): any;
/** Annoates a class field with an explicit offset. */
declare function offset(offset: usize): any;

@ -1,44 +1,92 @@
export class Array<T> {
private ptr: usize;
readonly capacity: i32;
private __memory: usize;
private __capacity: i32;
length: i32;
constructor(capacity: i32 = 0) {
if (capacity < 0)
throw new RangeError("invalid array length");
this.capacity = this.length = capacity;
if (capacity > 0) {
this.ptr = Heap.allocate(<usize>capacity);
} else {
this.ptr = 0;
}
this.__capacity = this.length = capacity;
this.__memory = capacity > 0 ? Heap.allocate(<usize>capacity * sizeof<T>()) : 0;
}
@operator("[]")
private __get(index: i32): T {
assert(index > 0 && index < this.capacity);
throw new Error("not implemented");
if (<u32>index >= this.__capacity)
throw new RangeError("index out of range");
return load<T>(this.__memory + <usize>index * sizeof<T>());
}
@operator("[]=")
private __set(index: i32, value: T): void {
assert(index > 0 && index < this.capacity);
throw new Error("not implemented");
if (<u32>index >= this.__capacity)
throw new RangeError("index out of range");
store<T>(this.__memory + <usize>index * sizeof<T>(), value);
}
dispose(): void {
store<i64>(changetype<usize>(this), 0);
Heap.dispose(this.ptr);
this.ptr = 0;
Heap.dispose(changetype<usize>(this));
indexOf(searchElement: T, fromIndex: i32 = 0): i32 {
if (<u32>fromIndex >= this.__capacity)
throw new RangeError("fromIndex out of range");
for (var index: usize = <usize>fromIndex, length: usize = min<u32>(this.length, this.__capacity); index < length; ++index)
if (load<T>(this.__memory + index * sizeof<T>()) == searchElement)
return index;
return -1;
}
// TODO
private __grow(capacity: i32): void {
assert(capacity > this.__capacity);
var newMemory = Heap.allocate(<usize>(capacity * sizeof<T>()));
if (this.__memory)
Heap.copy(newMemory, this.__memory, this.__capacity * sizeof<T>());
Heap.dispose(this.__memory);
this.__memory = newMemory;
this.__capacity = capacity;
}
push(element: T): i32 {
if (<u32>this.length >= this.__capacity)
this.__grow(max(this.length + 1, this.__capacity * 2));
store<T>(this.__memory + <usize>this.length * sizeof<T>(), element);
return ++this.length;
}
pop(): T {
if (this.length < 1 || <u32>this.length > this.__capacity)
throw new RangeError("index out of range");
--this.length;
return load<T>(this.__memory + <usize>this.length * sizeof<T>());
}
shift(): T {
if (this.length < 1 || <u32>this.length > this.__capacity)
throw new RangeError("index out of range");
var element = load<T>(this.__memory);
Heap.copy(this.__memory, this.__memory + sizeof<T>(), (this.__capacity - 1) * sizeof<T>());
Heap.fill(this.__memory + (this.__capacity - 1) * sizeof<T>(), 0, sizeof<T>());
--this.length;
return element;
}
unshift(element: T): i32 {
var capacity = this.__capacity;
if (<u32>this.length >= capacity)
this.__grow(max(this.length + 1, capacity * 2));
// FIXME: needs memmove (Heap.copy is just memcpy). it's also inefficient because
// __grow copies and then unshift copies again.
// Heap.copy(this.__memory + sizeof<T>(), this.__memory, capacity * sizeof<T>());
if (capacity)
for (var index: usize = capacity; index > 0; --index)
store<T>(this.__memory + index * sizeof<T>(), load<T>(this.__memory + (index - 1) * sizeof<T>()));
store<T>(this.__memory, element);
return ++this.length;
}
}
@struct
@explicit
export class CArray<T> {
private constructor() {}

@ -72,11 +72,15 @@ glob.sync(filter, { cwd: __dirname + "/compiler" }).forEach(filename => {
var wasmModule = new WebAssembly.Module(binary);
var wasmInstance = new WebAssembly.Instance(wasmModule, {
env: {
externalFunc: function() {},
externalFunc: function(arg0, arg1, arg2) {
console.log("env.externalFunc called with: " + arg0 + ", " + arg1 + ", " + arg2);
},
externalConst: 1
},
external: {
externalFunc: function() {},
externalFunc: function(arg0, arg1, arg2) {
console.log("external.externalFunc called with: " + arg0 + ", " + arg1 + ", " + arg2);
},
externalConst: 2
}
});

@ -10,7 +10,7 @@ class Animal<T> {
instanceSub<T>(a: T, b: T): T { return a - b + <T>Animal.ONE; } // tsc does not allow this
}
assert(sizeof<Animal<f64>>() == 7);
assert(sizeof<Animal<f64>>() == sizeof<usize>());
Animal.ONE;
Animal.add(1,2);

@ -145,8 +145,8 @@
(if
(i32.eqz
(i32.eq
(i32.const 7)
(i32.const 7)
(i32.const 4)
(i32.const 4)
)
)
(unreachable)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -1 +1,75 @@
// Array.fromPtr<i32>(1);
var arr = changetype<i32[]>(Heap.allocate(sizeof<usize>() + 2 * sizeof<i32>()));
assert(arr.length == 0);
assert(arr.__capacity == 0);
arr.push(42);
assert(arr[0] == 42);
assert(arr.length == 1);
assert(arr.__capacity == 1);
var i = arr.pop();
assert(i == 42);
assert(arr.length == 0);
assert(arr.__capacity == 1);
arr.push(43);
assert(arr.length == 1);
assert(arr.__capacity == 1);
assert(arr[0] == 43);
arr.push(44);
assert(arr.length == 2);
assert(arr.__capacity == 2);
assert(arr[0] == 43);
assert(arr[1] == 44);
arr.push(45);
assert(arr.length == 3);
assert(arr.__capacity == 4);
assert(arr[0] == 43);
assert(arr[1] == 44);
assert(arr[2] == 45);
arr.unshift(42); // see FIXME in std:array
assert(arr.length == 4);
assert(arr.__capacity == 4);
assert(arr[0] == 42);
assert(arr[1] == 43);
assert(arr[2] == 44);
assert(arr[3] == 45);
arr.unshift(41);
assert(arr.length == 5);
assert(arr.__capacity == 8);
assert(arr[0] == 41);
assert(arr[1] == 42);
assert(arr[2] == 43);
assert(arr[3] == 44);
assert(arr[4] == 45);
i = arr.shift();
assert(i == 41);
assert(arr.length == 4);
assert(arr.__capacity == 8);
assert(arr[0] == 42);
assert(arr[1] == 43);
assert(arr[2] == 44);
assert(arr[3] == 45);
i = arr.pop();
assert(i == 45);
assert(arr.length == 3);
assert(arr.__capacity == 8);
assert(arr[0] == 42);
assert(arr[1] == 43);
assert(arr[2] == 44);

File diff suppressed because it is too large Load Diff

@ -0,0 +1,157 @@
(module
(type $iii (func (param i32 i32) (result i32)))
(type $iiiv (func (param i32 i32 i32)))
(type $v (func))
(global $std/carray/arr (mut i32) (i32.const 0))
(global $HEAP_BASE i32 (i32.const 4))
(memory $0 1)
(export "memory" (memory $0))
(start $start)
(func $std:array/CArray#__get (; 0 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(i32.load
(i32.add
(get_local $0)
(i32.mul
(get_local $1)
(i32.const 4)
)
)
)
)
(func $std:array/CArray#__set (; 1 ;) (type $iiiv) (param $0 i32) (param $1 i32) (param $2 i32)
(i32.store
(i32.add
(get_local $0)
(i32.mul
(get_local $1)
(i32.const 4)
)
)
(get_local $2)
)
)
(func $start (; 2 ;) (type $v)
(local $0 i32)
(set_global $std/carray/arr
(get_global $HEAP_BASE)
)
(if
(i32.load
(get_global $HEAP_BASE)
)
(unreachable)
)
(if
(i32.load
(i32.add
(get_global $HEAP_BASE)
(i32.const 4)
)
)
(unreachable)
)
(if
(call $std:array/CArray#__get
(get_global $std/carray/arr)
(i32.const 0)
)
(unreachable)
)
(if
(call $std:array/CArray#__get
(get_global $std/carray/arr)
(i32.const 1)
)
(unreachable)
)
(call $std:array/CArray#__set
(get_global $std/carray/arr)
(i32.const 0)
(i32.const 42)
)
(call $std:array/CArray#__set
(get_global $std/carray/arr)
(i32.const 1)
(i32.const 24)
)
(if
(i32.ne
(i32.load
(get_global $HEAP_BASE)
)
(i32.const 42)
)
(unreachable)
)
(if
(i32.ne
(i32.load
(i32.add
(get_global $HEAP_BASE)
(i32.const 4)
)
)
(i32.const 24)
)
(unreachable)
)
(if
(i32.ne
(call $std:array/CArray#__get
(get_global $std/carray/arr)
(i32.const 0)
)
(i32.const 42)
)
(unreachable)
)
(if
(i32.ne
(call $std:array/CArray#__get
(get_global $std/carray/arr)
(i32.const 1)
)
(i32.const 24)
)
(unreachable)
)
(if
(block (result i32)
(call $std:array/CArray#__set
(get_global $std/carray/arr)
(i32.const 3)
(tee_local $0
(i32.const 9000)
)
)
(i32.ne
(get_local $0)
(i32.const 9000)
)
)
(unreachable)
)
(if
(i32.ne
(i32.load
(i32.add
(get_global $HEAP_BASE)
(i32.const 12)
)
)
(i32.const 9000)
)
(unreachable)
)
(if
(i32.ne
(call $std:array/CArray#__get
(get_global $std/carray/arr)
(i32.const 3)
)
(i32.const 9000)
)
(unreachable)
)
)
)

@ -0,0 +1,26 @@
// TBD: While this is useful as long as the pointer is just a local or global,
// things go haywire, compared to C, as soon as the CArray is a member of a
// class. Also, multi dimensional arrays cannot be implemented C-like because
// their length isn't known at compile time.
var arr: CArray<i32> = changetype<CArray<i32>>(HEAP_BASE);
assert(load<i32>(HEAP_BASE) == 0);
assert(load<i32>(HEAP_BASE + 4) == 0);
assert(arr[0] == 0);
assert(arr[1] == 0);
arr[0] = 42;
arr[1] = 24;
assert(load<i32>(HEAP_BASE) == 42);
assert(load<i32>(HEAP_BASE + 4) == 24);
assert(arr[0] == 42);
assert(arr[1] == 24);
assert((arr[3] = 9000) == 9000);
assert(load<i32>(HEAP_BASE + 12) == 9000);
assert(arr[3] == 9000);

@ -0,0 +1,287 @@
(module
(type $iii (func (param i32 i32) (result i32)))
(type $iiiv (func (param i32 i32 i32)))
(type $v (func))
(global $std/carray/arr (mut i32) (i32.const 0))
(global $HEAP_BASE i32 (i32.const 4))
(memory $0 1)
(export "memory" (memory $0))
(start $start)
(func $std:array/CArray#__get (; 0 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(return
(i32.load
(i32.add
(get_local $0)
(i32.mul
(get_local $1)
(i32.const 4)
)
)
)
)
)
(func $std:array/CArray#__set (; 1 ;) (type $iiiv) (param $0 i32) (param $1 i32) (param $2 i32)
(i32.store
(i32.add
(get_local $0)
(i32.mul
(get_local $1)
(i32.const 4)
)
)
(get_local $2)
)
)
(func $start (; 2 ;) (type $v)
(local $0 i32)
(set_global $std/carray/arr
(get_global $HEAP_BASE)
)
(if
(i32.eqz
(i32.eq
(i32.load
(get_global $HEAP_BASE)
)
(i32.const 0)
)
)
(unreachable)
)
(if
(i32.eqz
(i32.eq
(i32.load
(i32.add
(get_global $HEAP_BASE)
(i32.const 4)
)
)
(i32.const 0)
)
)
(unreachable)
)
(if
(i32.eqz
(i32.eq
(call $std:array/CArray#__get
(get_global $std/carray/arr)
(i32.const 0)
)
(i32.const 0)
)
)
(unreachable)
)
(if
(i32.eqz
(i32.eq
(call $std:array/CArray#__get
(get_global $std/carray/arr)
(i32.const 1)
)
(i32.const 0)
)
)
(unreachable)
)
(call $std:array/CArray#__set
(get_global $std/carray/arr)
(i32.const 0)
(i32.const 42)
)
(call $std:array/CArray#__set
(get_global $std/carray/arr)
(i32.const 1)
(i32.const 24)
)
(if
(i32.eqz
(i32.eq
(i32.load
(get_global $HEAP_BASE)
)
(i32.const 42)
)
)
(unreachable)
)
(if
(i32.eqz
(i32.eq
(i32.load
(i32.add
(get_global $HEAP_BASE)
(i32.const 4)
)
)
(i32.const 24)
)
)
(unreachable)
)
(if
(i32.eqz
(i32.eq
(call $std:array/CArray#__get
(get_global $std/carray/arr)
(i32.const 0)
)
(i32.const 42)
)
)
(unreachable)
)
(if
(i32.eqz
(i32.eq
(call $std:array/CArray#__get
(get_global $std/carray/arr)
(i32.const 1)
)
(i32.const 24)
)
)
(unreachable)
)
(if
(i32.eqz
(i32.eq
(block (result i32)
(call $std:array/CArray#__set
(get_global $std/carray/arr)
(i32.const 3)
(tee_local $0
(i32.const 9000)
)
)
(get_local $0)
)
(i32.const 9000)
)
)
(unreachable)
)
(if
(i32.eqz
(i32.eq
(i32.load
(i32.add
(get_global $HEAP_BASE)
(i32.const 12)
)
)
(i32.const 9000)
)
)
(unreachable)
)
(if
(i32.eqz
(i32.eq
(call $std:array/CArray#__get
(get_global $std/carray/arr)
(i32.const 3)
)
(i32.const 9000)
)
)
(unreachable)
)
)
)
(;
[program.elements]
GLOBAL: NaN
GLOBAL: Infinity
FUNCTION_PROTOTYPE: isNaN
FUNCTION_PROTOTYPE: isFinite
FUNCTION_PROTOTYPE: clz
FUNCTION_PROTOTYPE: ctz
FUNCTION_PROTOTYPE: popcnt
FUNCTION_PROTOTYPE: rotl
FUNCTION_PROTOTYPE: rotr
FUNCTION_PROTOTYPE: abs
FUNCTION_PROTOTYPE: max
FUNCTION_PROTOTYPE: min
FUNCTION_PROTOTYPE: ceil
FUNCTION_PROTOTYPE: floor
FUNCTION_PROTOTYPE: copysign
FUNCTION_PROTOTYPE: nearest
FUNCTION_PROTOTYPE: reinterpret
FUNCTION_PROTOTYPE: sqrt
FUNCTION_PROTOTYPE: trunc
FUNCTION_PROTOTYPE: load
FUNCTION_PROTOTYPE: store
FUNCTION_PROTOTYPE: sizeof
FUNCTION_PROTOTYPE: select
FUNCTION_PROTOTYPE: unreachable
FUNCTION_PROTOTYPE: current_memory
FUNCTION_PROTOTYPE: grow_memory
FUNCTION_PROTOTYPE: changetype
FUNCTION_PROTOTYPE: assert
FUNCTION_PROTOTYPE: i8
FUNCTION_PROTOTYPE: i16
FUNCTION_PROTOTYPE: i32
FUNCTION_PROTOTYPE: i64
FUNCTION_PROTOTYPE: u8
FUNCTION_PROTOTYPE: u16
FUNCTION_PROTOTYPE: u32
FUNCTION_PROTOTYPE: u64
FUNCTION_PROTOTYPE: bool
FUNCTION_PROTOTYPE: f32
FUNCTION_PROTOTYPE: f64
FUNCTION_PROTOTYPE: isize
FUNCTION_PROTOTYPE: usize
GLOBAL: HEAP_BASE
CLASS_PROTOTYPE: std:array/Array
CLASS_PROTOTYPE: Array
CLASS_PROTOTYPE: std:array/CArray
CLASS_PROTOTYPE: CArray
CLASS_PROTOTYPE: std:error/Error
CLASS_PROTOTYPE: Error
CLASS_PROTOTYPE: std:error/RangeError
CLASS_PROTOTYPE: RangeError
GLOBAL: std:heap/ALIGN_LOG2
GLOBAL: std:heap/ALIGN_SIZE
GLOBAL: std:heap/ALIGN_MASK
GLOBAL: std:heap/HEAP_OFFSET
CLASS_PROTOTYPE: std:heap/Heap
CLASS_PROTOTYPE: Heap
PROPERTY: std:heap/Heap.used
PROPERTY: std:heap/Heap.free
PROPERTY: std:heap/Heap.size
FUNCTION_PROTOTYPE: std:heap/Heap.allocate
FUNCTION_PROTOTYPE: std:heap/Heap.dispose
FUNCTION_PROTOTYPE: std:heap/Heap.copy
FUNCTION_PROTOTYPE: std:heap/Heap.fill
FUNCTION_PROTOTYPE: std:heap/Heap.compare
CLASS_PROTOTYPE: std:map/Map
CLASS_PROTOTYPE: Map
CLASS_PROTOTYPE: std:regexp/RegExp
CLASS_PROTOTYPE: RegExp
CLASS_PROTOTYPE: std:set/Set
CLASS_PROTOTYPE: Set
GLOBAL: std:string/EMPTY
CLASS_PROTOTYPE: std:string/String
CLASS_PROTOTYPE: String
FUNCTION_PROTOTYPE: std:string/isWhiteSpaceOrLineTerminator
FUNCTION_PROTOTYPE: std:string/parseInt
FUNCTION_PROTOTYPE: parseInt
FUNCTION_PROTOTYPE: std:string/parseFloat
FUNCTION_PROTOTYPE: parseFloat
GLOBAL: std/carray/arr
[program.exports]
CLASS_PROTOTYPE: std:array/Array
CLASS_PROTOTYPE: std:array/CArray
CLASS_PROTOTYPE: std:error/Error
CLASS_PROTOTYPE: std:error/RangeError
CLASS_PROTOTYPE: std:heap/Heap
CLASS_PROTOTYPE: std:map/Map
CLASS_PROTOTYPE: std:regexp/RegExp
CLASS_PROTOTYPE: std:set/Set
CLASS_PROTOTYPE: std:string/String
FUNCTION_PROTOTYPE: std:string/parseInt
FUNCTION_PROTOTYPE: std:string/parseFloat
;)