mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-05-08 13:22:16 +00:00
Initial element access compilation; Carefully approaching std array
This commit is contained in:
parent
dd596b015d
commit
2c009c67d3
12
src/ast.ts
12
src/ast.ts
@ -363,12 +363,12 @@ export abstract class Node {
|
|||||||
stmt.decoratorKind = DecoratorKind.OPERATOR;
|
stmt.decoratorKind = DecoratorKind.OPERATOR;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "struct":
|
case "explicit":
|
||||||
stmt.decoratorKind = DecoratorKind.STRUCT;
|
stmt.decoratorKind = DecoratorKind.EXPLICIT;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "size":
|
case "offset":
|
||||||
stmt.decoratorKind = DecoratorKind.SIZE;
|
stmt.decoratorKind = DecoratorKind.OFFSET;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -1382,8 +1382,8 @@ export const enum DecoratorKind {
|
|||||||
CUSTOM,
|
CUSTOM,
|
||||||
GLOBAL,
|
GLOBAL,
|
||||||
OPERATOR,
|
OPERATOR,
|
||||||
STRUCT,
|
EXPLICIT,
|
||||||
SIZE
|
OFFSET
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Depresents a decorator. */
|
/** Depresents a decorator. */
|
||||||
|
@ -1349,7 +1349,7 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty
|
|||||||
}
|
}
|
||||||
arg0 = compiler.compileExpression(operands[0], usizeType);
|
arg0 = compiler.compileExpression(operands[0], usizeType);
|
||||||
compiler.currentType = typeArguments[0];
|
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
|
case "store": // store<T?>(offset: usize, value: T) -> void
|
||||||
compiler.currentType = Type.void;
|
compiler.currentType = Type.void;
|
||||||
@ -1372,7 +1372,7 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty
|
|||||||
}
|
}
|
||||||
type = compiler.currentType;
|
type = compiler.currentType;
|
||||||
compiler.currentType = Type.void;
|
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
|
case "sizeof": // sizeof<T!>() -> usize
|
||||||
compiler.currentType = usizeType;
|
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);
|
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();
|
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:
|
default:
|
||||||
this.error(DiagnosticCode.Operation_not_supported, expression.range);
|
this.error(DiagnosticCode.Operation_not_supported, expression.range);
|
||||||
return this.module.createUnreachable();
|
return this.module.createUnreachable();
|
||||||
@ -2275,11 +2285,11 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
this.currentType = tee ? (<Field>element).type : Type.void;
|
this.currentType = tee ? (<Field>element).type : Type.void;
|
||||||
var elementNativeType = (<Field>element).type.toNativeType();
|
var elementNativeType = (<Field>element).type.toNativeType();
|
||||||
if (!tee)
|
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);
|
tempLocal = this.currentFunction.getAndFreeTempLocal((<Field>element).type);
|
||||||
return this.module.createBlock(null, [ // TODO: simplify if valueWithCorrectType has no side effects
|
return this.module.createBlock(null, [ // TODO: simplify if valueWithCorrectType has no side effects
|
||||||
this.module.createSetLocal(tempLocal.index, valueWithCorrectType),
|
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)
|
this.module.createGetLocal(tempLocal.index, elementNativeType)
|
||||||
], elementNativeType);
|
], elementNativeType);
|
||||||
|
|
||||||
@ -2325,6 +2335,38 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
} else
|
} else
|
||||||
this.error(DiagnosticCode.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property, expression.range, (<Property>element).internalName);
|
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();
|
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);
|
this.error(DiagnosticCode.Operation_not_supported, expression.range);
|
||||||
return this.module.createUnreachable();
|
return this.module.createUnreachable();
|
||||||
@ -2465,8 +2507,11 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
var resolved = this.program.resolveElementAccess(expression, this.currentFunction); // reports
|
var resolved = this.program.resolveElementAccess(expression, this.currentFunction); // reports
|
||||||
if (!resolved)
|
if (!resolved)
|
||||||
return this.module.createUnreachable();
|
return this.module.createUnreachable();
|
||||||
|
assert(resolved.element.kind == ElementKind.FUNCTION_PROTOTYPE && resolved.target && resolved.target.kind == ElementKind.CLASS);
|
||||||
throw new Error("not implemented"); // TODO
|
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 {
|
compileIdentifierExpression(expression: IdentifierExpression, contextualType: Type): ExpressionRef {
|
||||||
@ -2632,7 +2677,7 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
assert((<Field>element).memoryOffset >= 0);
|
assert((<Field>element).memoryOffset >= 0);
|
||||||
targetExpr = this.compileExpression(<Expression>resolved.targetExpression, this.options.target == Target.WASM64 ? Type.usize64 : Type.usize32);
|
targetExpr = this.compileExpression(<Expression>resolved.targetExpression, this.options.target == Target.WASM64 ? Type.usize64 : Type.usize32);
|
||||||
this.currentType = (<Field>element).type;
|
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,
|
targetExpr,
|
||||||
(<Field>element).type.toNativeType(),
|
(<Field>element).type.toNativeType(),
|
||||||
(<Field>element).memoryOffset
|
(<Field>element).memoryOffset
|
||||||
|
@ -1,8 +1,14 @@
|
|||||||
// internal naming scheme
|
// internal naming scheme
|
||||||
|
|
||||||
|
/** Path delimited inserted between file system levels. */
|
||||||
export const PATH_DELIMITER = "/";
|
export const PATH_DELIMITER = "/";
|
||||||
|
/** Substitution used to indicate the parent directory. */
|
||||||
export const PARENT_SUBST = "..";
|
export const PARENT_SUBST = "..";
|
||||||
|
/** Function name prefix used for getters. */
|
||||||
export const GETTER_PREFIX = "get:";
|
export const GETTER_PREFIX = "get:";
|
||||||
|
/** Function name prefix used for setters. */
|
||||||
export const SETTER_PREFIX = "set:";
|
export const SETTER_PREFIX = "set:";
|
||||||
|
/** Delimiter used between class names and instance members. */
|
||||||
export const INSTANCE_DELIMITER = "#";
|
export const INSTANCE_DELIMITER = "#";
|
||||||
|
/** Delimited used between class and namespace names and static members. */
|
||||||
export const STATIC_DELIMITER = ".";
|
export const STATIC_DELIMITER = ".";
|
||||||
|
@ -79,6 +79,7 @@ export enum DiagnosticCode {
|
|||||||
Export_declaration_conflicts_with_exported_declaration_of_0 = 2484,
|
Export_declaration_conflicts_with_exported_declaration_of_0 = 2484,
|
||||||
Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property = 2540,
|
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,
|
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_0_arguments_but_got_1 = 2554,
|
||||||
Expected_at_least_0_arguments_but_got_1 = 2555,
|
Expected_at_least_0_arguments_but_got_1 = 2555,
|
||||||
Expected_0_type_arguments_but_got_1 = 2558,
|
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 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 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 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 2554: return "Expected {0} arguments, but got {1}.";
|
||||||
case 2555: return "Expected at least {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}.";
|
case 2558: return "Expected {0} type arguments, but got {1}.";
|
||||||
|
@ -79,6 +79,7 @@
|
|||||||
"Export declaration conflicts with exported declaration of '{0}'.": 2484,
|
"Export declaration conflicts with exported declaration of '{0}'.": 2484,
|
||||||
"Cannot assign to '{0}' because it is a constant or a read-only property.": 2540,
|
"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,
|
"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 {0} arguments, but got {1}.": 2554,
|
||||||
"Expected at least {0} arguments, but got {1}.": 2555,
|
"Expected at least {0} arguments, but got {1}.": 2555,
|
||||||
"Expected {0} type arguments, but got {1}.": 2558,
|
"Expected {0} type arguments, but got {1}.": 2558,
|
||||||
|
@ -1580,6 +1580,19 @@ export class Parser extends DiagnosticEmitter {
|
|||||||
|
|
||||||
var startPos = expr.range.start;
|
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
|
// CallExpression
|
||||||
var typeArguments = this.tryParseTypeArgumentsBeforeArguments(tn); // skips '(' on success
|
var typeArguments = this.tryParseTypeArgumentsBeforeArguments(tn); // skips '(' on success
|
||||||
// there might be better ways to distinguish a LESSTHAN from a CALL with type arguments
|
// 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 token: Token;
|
||||||
var next: Expression | null = null;
|
var next: Expression | null = null;
|
||||||
var nextPrecedence: Precedence;
|
var nextPrecedence: Precedence;
|
||||||
|
|
||||||
while ((nextPrecedence = determinePrecedence(token = tn.peek())) >= precedence) { // precedence climbing
|
while ((nextPrecedence = determinePrecedence(token = tn.peek())) >= precedence) { // precedence climbing
|
||||||
tn.next();
|
tn.next();
|
||||||
|
|
||||||
@ -1604,19 +1616,6 @@ export class Parser extends DiagnosticEmitter {
|
|||||||
return null;
|
return null;
|
||||||
expr = Node.createAssertionExpression(AssertionKind.AS, expr, toType, tn.range(startPos, tn.pos));
|
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
|
// UnaryPostfixExpression
|
||||||
} else if (token == Token.PLUS_PLUS || token == Token.MINUS_MINUS) {
|
} else if (token == Token.PLUS_PLUS || token == Token.MINUS_MINUS) {
|
||||||
if (expr.kind != NodeKind.IDENTIFIER && expr.kind != NodeKind.ELEMENTACCESS && expr.kind != NodeKind.PROPERTYACCESS)
|
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);
|
this.checkGlobalAlias(prototype, declaration);
|
||||||
|
|
||||||
if (hasDecorator("struct", declaration.decorators)) {
|
if (hasDecorator("explicit", declaration.decorators)) {
|
||||||
prototype.isStruct = true;
|
prototype.isExplicit = true;
|
||||||
if (declaration.implementsTypes && declaration.implementsTypes.length)
|
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));
|
this.error(DiagnosticCode.Structs_cannot_implement_interfaces, Range.join(declaration.name.range, declaration.implementsTypes[declaration.implementsTypes.length - 1].range));
|
||||||
} else if (declaration.implementsTypes.length)
|
} else if (declaration.implementsTypes.length)
|
||||||
@ -413,8 +413,8 @@ export class Program extends DiagnosticEmitter {
|
|||||||
} else
|
} else
|
||||||
classPrototype.instanceMembers = new Map();
|
classPrototype.instanceMembers = new Map();
|
||||||
instancePrototype = new FunctionPrototype(this, name, internalName, declaration, classPrototype);
|
instancePrototype = new FunctionPrototype(this, name, internalName, declaration, classPrototype);
|
||||||
// if (classPrototype.isStruct && instancePrototype.isAbstract) {
|
// if (classPrototype.isExplicit && instancePrototype.isAbstract) {
|
||||||
// this.error( Structs cannot declare abstract methods. );
|
// this.error( Explicit classes cannot declare abstract methods. );
|
||||||
// }
|
// }
|
||||||
classPrototype.instanceMembers.set(name, instancePrototype);
|
classPrototype.instanceMembers.set(name, instancePrototype);
|
||||||
}
|
}
|
||||||
@ -436,19 +436,19 @@ export class Program extends DiagnosticEmitter {
|
|||||||
switch ((<StringLiteralExpression>firstArg).value) {
|
switch ((<StringLiteralExpression>firstArg).value) {
|
||||||
|
|
||||||
case "[]":
|
case "[]":
|
||||||
classPrototype.opIndexedGet = instancePrototype;
|
classPrototype.fnIndexedGet = instancePrototype.simpleName;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "[]=":
|
case "[]=":
|
||||||
classPrototype.opIndexedSet = instancePrototype;
|
classPrototype.fnIndexedSet = instancePrototype.simpleName;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "+":
|
case "+":
|
||||||
classPrototype.opConcat = instancePrototype;
|
classPrototype.fnConcat = instancePrototype.simpleName;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "==":
|
case "==":
|
||||||
classPrototype.opEquals = instancePrototype;
|
classPrototype.fnEquals = instancePrototype.simpleName;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default: // TBD: does it make sense to provide more, even though not JS/TS-compatible?
|
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;
|
var target = resolvedElement.element;
|
||||||
switch (target.kind) {
|
switch (target.kind) {
|
||||||
|
|
||||||
// TBD: should indexed access on static classes, like `Heap`, be a supported as well?
|
case ElementKind.GLOBAL:
|
||||||
case ElementKind.CLASS:
|
case ElementKind.LOCAL:
|
||||||
var type = (<Class>target).type;
|
case ElementKind.FIELD:
|
||||||
|
var type = (<VariableLikeElement>target).type;
|
||||||
if (type.classType) {
|
if (type.classType) {
|
||||||
var indexedGet: FunctionPrototype | null;
|
var indexedGetName = (target = type.classType).prototype.fnIndexedGet;
|
||||||
if (indexedGet = (target = type.classType).prototype.opIndexedGet)
|
var indexedGet: Element | null;
|
||||||
return resolvedElement.set(indexedGet).withTarget(target, targetExpression);
|
if (indexedGetName != null && target.members && (indexedGet = target.members.get(indexedGetName)) && indexedGet.kind == ElementKind.FUNCTION_PROTOTYPE)
|
||||||
|
return resolvedElement.set(indexedGet).withTarget(type.classType, targetExpression);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1221,8 +1223,8 @@ export enum ElementFlags {
|
|||||||
PRIVATE = 1 << 15,
|
PRIVATE = 1 << 15,
|
||||||
/** Is an abstract member. */
|
/** Is an abstract member. */
|
||||||
ABSTRACT = 1 << 16,
|
ABSTRACT = 1 << 16,
|
||||||
/** Is a struct-like class with limited capabilites. */
|
/** Is an explicitly layed out and allocated class with limited capabilites. */
|
||||||
STRUCT = 1 << 17,
|
EXPLICIT = 1 << 17,
|
||||||
/** Has already inherited base class static members. */
|
/** Has already inherited base class static members. */
|
||||||
HAS_STATIC_BASE_MEMBERS = 1 << 18
|
HAS_STATIC_BASE_MEMBERS = 1 << 18
|
||||||
}
|
}
|
||||||
@ -1877,13 +1879,13 @@ export class ClassPrototype extends Element {
|
|||||||
basePrototype: ClassPrototype | null = null; // set in Program#initialize
|
basePrototype: ClassPrototype | null = null; // set in Program#initialize
|
||||||
|
|
||||||
/** Overloaded indexed get method, if any. */
|
/** 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. */
|
/** Overloaded indexed set method, if any. */
|
||||||
opIndexedSet: FunctionPrototype | null = null;
|
fnIndexedSet: string | null = null;
|
||||||
/** Overloaded concatenation method, if any. */
|
/** Overloaded concatenation method, if any. */
|
||||||
opConcat: FunctionPrototype | null = null;
|
fnConcat: string | null = null;
|
||||||
/** Overloaded equality comparison method, if any. */
|
/** 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) {
|
constructor(program: Program, simpleName: string, internalName: string, declaration: ClassDeclaration | null = null) {
|
||||||
super(program, simpleName, internalName);
|
super(program, simpleName, internalName);
|
||||||
@ -1903,9 +1905,9 @@ export class ClassPrototype extends Element {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Whether a struct-like class with limited capabilities or not. */
|
/** Whether explicitly layed out and allocated */
|
||||||
get isStruct(): bool { return (this.flags & ElementFlags.STRUCT) != 0; }
|
get isExplicit(): bool { return (this.flags & ElementFlags.EXPLICIT) != 0; }
|
||||||
set isStruct(is: bool) { if (is) this.flags |= ElementFlags.STRUCT; else this.flags &= ~ElementFlags.STRUCT; }
|
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 {
|
resolve(typeArguments: Type[] | null, contextualTypeArguments: Map<string,Type> | null = null): Class | null {
|
||||||
var instanceKey = typeArguments ? typesToString(typeArguments) : "";
|
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);
|
this.program.error(DiagnosticCode.A_class_may_only_extend_another_class, declaration.extendsType.range);
|
||||||
return null;
|
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));
|
this.program.error(DiagnosticCode.Structs_cannot_extend_classes_and_vice_versa, Range.join(declaration.name.range, declaration.extendsType.range));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -1957,9 +1959,9 @@ export class ClassPrototype extends Element {
|
|||||||
instance.contextualTypeArguments = contextualTypeArguments;
|
instance.contextualTypeArguments = contextualTypeArguments;
|
||||||
this.instances.set(instanceKey, instance);
|
this.instances.set(instanceKey, instance);
|
||||||
|
|
||||||
var memoryOffset: i32 = 0;
|
var memoryOffset: u32 = 0;
|
||||||
if (baseClass) {
|
if (baseClass) {
|
||||||
memoryOffset = baseClass.type.byteSize;
|
memoryOffset = baseClass.currentMemoryOffset;
|
||||||
if (baseClass.members) {
|
if (baseClass.members) {
|
||||||
if (!instance.members)
|
if (!instance.members)
|
||||||
instance.members = new Map();
|
instance.members = new Map();
|
||||||
@ -1983,7 +1985,7 @@ export class ClassPrototype extends Element {
|
|||||||
var fieldType = this.program.resolveType(fieldDeclaration.type, instance.contextualTypeArguments); // reports
|
var fieldType = this.program.resolveType(fieldDeclaration.type, instance.contextualTypeArguments); // reports
|
||||||
if (fieldType) {
|
if (fieldType) {
|
||||||
var fieldInstance = new Field(<FieldPrototype>member, (<FieldPrototype>member).internalName, 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 1: break;
|
||||||
case 2: if (memoryOffset & 1) ++memoryOffset; break;
|
case 2: if (memoryOffset & 1) ++memoryOffset; break;
|
||||||
case 4: if (memoryOffset & 3) memoryOffset = (memoryOffset | 3) + 1; 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;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2051,6 +2053,8 @@ export class Class extends Element {
|
|||||||
base: Class | null;
|
base: Class | null;
|
||||||
/** Contextual type arguments for fields and methods. */
|
/** Contextual type arguments for fields and methods. */
|
||||||
contextualTypeArguments: Map<string,Type> | null = null;
|
contextualTypeArguments: Map<string,Type> | null = null;
|
||||||
|
/** Current member memory offset. */
|
||||||
|
currentMemoryOffset: u32 = 0;
|
||||||
|
|
||||||
/** Constructs a new class. */
|
/** Constructs a new class. */
|
||||||
constructor(prototype: ClassPrototype, internalName: string, typeArguments: Type[] | null = null, base: Class | null = null) {
|
constructor(prototype: ClassPrototype, internalName: string, typeArguments: Type[] | null = null, base: Class | null = null) {
|
||||||
|
@ -76,8 +76,8 @@ export class Type {
|
|||||||
/** Type flags. */
|
/** Type flags. */
|
||||||
flags: TypeFlags;
|
flags: TypeFlags;
|
||||||
/** Size in bits. */
|
/** Size in bits. */
|
||||||
size: i32;
|
size: u32;
|
||||||
/** Size in bytes. */
|
/** Size in bytes. Ceiled to 8-bits. */
|
||||||
byteSize: i32;
|
byteSize: i32;
|
||||||
/** Underlying class type, if a class type. */
|
/** Underlying class type, if a class type. */
|
||||||
classType: Class | null;
|
classType: Class | null;
|
||||||
|
19
std/assembly.d.ts
vendored
19
std/assembly.d.ts
vendored
@ -202,6 +202,12 @@ declare class Array<T> {
|
|||||||
constructor(capacity?: i32);
|
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. */
|
/** Class representing a sequence of characters. */
|
||||||
declare class String {
|
declare class String {
|
||||||
|
|
||||||
@ -281,13 +287,16 @@ interface Number {}
|
|||||||
interface Object {}
|
interface Object {}
|
||||||
interface RegExp {}
|
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;
|
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 operator(token: string): any;
|
||||||
|
|
||||||
declare function struct(target: Function): any;
|
/** Annotates a class as explicitly layed out and allocated. */
|
||||||
declare function size(size: usize): any;
|
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> {
|
export class Array<T> {
|
||||||
|
|
||||||
private ptr: usize;
|
private __memory: usize;
|
||||||
|
private __capacity: i32;
|
||||||
readonly capacity: i32;
|
|
||||||
length: i32;
|
length: i32;
|
||||||
|
|
||||||
constructor(capacity: i32 = 0) {
|
constructor(capacity: i32 = 0) {
|
||||||
if (capacity < 0)
|
if (capacity < 0)
|
||||||
throw new RangeError("invalid array length");
|
throw new RangeError("invalid array length");
|
||||||
this.capacity = this.length = capacity;
|
this.__capacity = this.length = capacity;
|
||||||
if (capacity > 0) {
|
this.__memory = capacity > 0 ? Heap.allocate(<usize>capacity * sizeof<T>()) : 0;
|
||||||
this.ptr = Heap.allocate(<usize>capacity);
|
|
||||||
} else {
|
|
||||||
this.ptr = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@operator("[]")
|
@operator("[]")
|
||||||
private __get(index: i32): T {
|
private __get(index: i32): T {
|
||||||
assert(index > 0 && index < this.capacity);
|
if (<u32>index >= this.__capacity)
|
||||||
throw new Error("not implemented");
|
throw new RangeError("index out of range");
|
||||||
|
return load<T>(this.__memory + <usize>index * sizeof<T>());
|
||||||
}
|
}
|
||||||
|
|
||||||
@operator("[]=")
|
@operator("[]=")
|
||||||
private __set(index: i32, value: T): void {
|
private __set(index: i32, value: T): void {
|
||||||
assert(index > 0 && index < this.capacity);
|
if (<u32>index >= this.__capacity)
|
||||||
throw new Error("not implemented");
|
throw new RangeError("index out of range");
|
||||||
|
store<T>(this.__memory + <usize>index * sizeof<T>(), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
dispose(): void {
|
indexOf(searchElement: T, fromIndex: i32 = 0): i32 {
|
||||||
store<i64>(changetype<usize>(this), 0);
|
if (<u32>fromIndex >= this.__capacity)
|
||||||
Heap.dispose(this.ptr);
|
throw new RangeError("fromIndex out of range");
|
||||||
this.ptr = 0;
|
for (var index: usize = <usize>fromIndex, length: usize = min<u32>(this.length, this.__capacity); index < length; ++index)
|
||||||
Heap.dispose(changetype<usize>(this));
|
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> {
|
export class CArray<T> {
|
||||||
|
|
||||||
private constructor() {}
|
private constructor() {}
|
||||||
|
@ -72,11 +72,15 @@ glob.sync(filter, { cwd: __dirname + "/compiler" }).forEach(filename => {
|
|||||||
var wasmModule = new WebAssembly.Module(binary);
|
var wasmModule = new WebAssembly.Module(binary);
|
||||||
var wasmInstance = new WebAssembly.Instance(wasmModule, {
|
var wasmInstance = new WebAssembly.Instance(wasmModule, {
|
||||||
env: {
|
env: {
|
||||||
externalFunc: function() {},
|
externalFunc: function(arg0, arg1, arg2) {
|
||||||
|
console.log("env.externalFunc called with: " + arg0 + ", " + arg1 + ", " + arg2);
|
||||||
|
},
|
||||||
externalConst: 1
|
externalConst: 1
|
||||||
},
|
},
|
||||||
external: {
|
external: {
|
||||||
externalFunc: function() {},
|
externalFunc: function(arg0, arg1, arg2) {
|
||||||
|
console.log("external.externalFunc called with: " + arg0 + ", " + arg1 + ", " + arg2);
|
||||||
|
},
|
||||||
externalConst: 2
|
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
|
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.ONE;
|
||||||
Animal.add(1,2);
|
Animal.add(1,2);
|
||||||
|
@ -145,8 +145,8 @@
|
|||||||
(if
|
(if
|
||||||
(i32.eqz
|
(i32.eqz
|
||||||
(i32.eq
|
(i32.eq
|
||||||
(i32.const 7)
|
(i32.const 4)
|
||||||
(i32.const 7)
|
(i32.const 4)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
(unreachable)
|
(unreachable)
|
||||||
|
2918
tests/compiler/std/array.optimized-inlined.wast
Normal file
2918
tests/compiler/std/array.optimized-inlined.wast
Normal file
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
157
tests/compiler/std/carray.optimized.wast
Normal file
157
tests/compiler/std/carray.optimized.wast
Normal file
@ -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)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
26
tests/compiler/std/carray.ts
Normal file
26
tests/compiler/std/carray.ts
Normal file
@ -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);
|
287
tests/compiler/std/carray.wast
Normal file
287
tests/compiler/std/carray.wast
Normal file
@ -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
|
||||||
|
;)
|
Loading…
x
Reference in New Issue
Block a user